Jump to content

Mind Rapist

Active Member
  • Posts

    548
  • Joined

  • Last visited

  • Days Won

    6
  • Feedback

    0%

Posts posted by Mind Rapist

  1. 4 hours ago, OtherChoice said:

    Ok after testing the original bugfix you linked i found the problem, the m_akSimplePlayerInfo array is filled in a wrong way this is the solution:
    In game/char.cpp at void CHARACTER::PointsPacket() change as following
     

    
    void CHARACTER::PointsPacket()
    {
    	if (!GetDesc())
    		return;
    
    	TPacketGCPoints pack;
    
    	pack.header	= HEADER_GC_CHARACTER_POINTS;
    
    	pack.points[POINT_LEVEL]		= GetLevel();
    	pack.points[POINT_EXP]		= GetExp();
    	pack.points[POINT_NEXT_EXP]		= GetNextExp();
    	pack.points[POINT_HP]		= GetHP();
    	pack.points[POINT_MAX_HP]		= GetMaxHP();
    	pack.points[POINT_SP]		= GetSP();
    	pack.points[POINT_MAX_SP]		= GetMaxSP();
    	pack.points[POINT_GOLD]		= GetGold();
    	pack.points[POINT_STAMINA]		= GetStamina();
    	pack.points[POINT_MAX_STAMINA]	= GetMaxStamina();
    	for (int i = POINT_ST; i < POINT_IQ + 1; ++i)
    		pack.points[i] = GetRealPoint(i);
    	
    	for (int i = POINT_IQ + 1; i < POINT_MAX_NUM; ++i)
    		pack.points[i] = GetPoint(i);
    
    	GetDesc()->Packet(&pack, sizeof(TPacketGCPoints));
    }

    then at void CHARACTER::Disconnect add this line

    
    if (GetDesc())
    	{
      		/*add this*/
    		PointsPacket();
      		/*        */
      
    		packet_point_change pack;
    		pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
    		pack.dwVID = m_vid;
    		pack.type = POINT_PLAYTIME;
    		pack.value = GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000;
    		pack.amount = 0;
    		GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
    		GetDesc()->BindCharacter(NULL);
    //		BindDesc(NULL);
    	}

    then in client source

    
    PythonNetworkStreamPhaseGame.cpp -> bool CPythonNetworkStream::RecvPointChange()  ------->Make these changes
    
    case POINT_PLAYTIME:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].dwPlayMinutes = PointChange.value;
    	break;
    case POINT_LEVEL:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byLevel = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    /*case POINT_ST:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byST = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    case POINT_DX:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byDX = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    case POINT_HT:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byHT = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    case POINT_IQ:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byIQ = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;*/
    case POINT_ST:
    case POINT_DX:
    case POINT_HT:
    case POINT_IQ:
    	__RefreshStatus();
    	__RefreshSkillWindow();
    break;

    Every other thing is like original thread 

    Tested and working.

    I removed all the extra steps and left only what the post shows. Then I followed the steps above for char.cpp and PythonNetworkStreamPhaseGame.cpp. The problem is still here with a little difference... Now the horse stats are displayed only when during the logout, character rides a horse. I just don't get it we tried everything.

    EDIT: Ok that was my bad actually. The problem is fixed!!!

    I forgot to change

    for (int i = POINT_ST; i < POINT_IQ + 1; ++i)
    	pack.points[i] = GetRealPoint(i);

    in PointsPacket()

    was:

    for (int i = POINT_ST; i < POINT_MAX_NUM; ++i)
    	pack.points[i] = GetPoint(i);

    but now I believe it's all right!

    Thank you so much for your time. Lots of respect not just for helping me find a solution but also for not giving up on me. Cheers to you man :)

  2. 10 hours ago, OtherChoice said:

    Oh ok i quite didn't get it, anyhow that's the solution you are looking for
     

    
    This is you void CHARACTER::Disconnect(...)
    //ADD this
    PointChange(POINT_ST, 0);
    PointChange(POINT_DX, 0);
    PointChange(POINT_IQ, 0);
    PointChange(POINT_HT, 0);
    //end
    
    packet_point_change pack;
    pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
    pack.dwVID = m_vid;
    pack.type = POINT_PLAYTIME;
    pack.value = GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000;
    pack.amount = 0;
    pack.point_ST = GetPoint(POINT_ST);
    pack.point_DX = GetPoint(POINT_DX);
    pack.point_HT = GetPoint(POINT_HT);
    pack.point_IQ = GetPoint(POINT_IQ);
    GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
    
    /*if (GetHorseST() > GetPoint(POINT_ST))
    	PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
    
    if (GetHorseDX() > GetPoint(POINT_DX))
    	PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));				REMOVE ALL THIS
    
    if (GetHorseHT() > GetPoint(POINT_HT))
    	PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
    
    if (GetHorseIQ() > GetPoint(POINT_IQ))
    	PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));*/ 

     

    Thanks. I tried it but it didn't work.

    I tried adding the PointChange(POINT_X, 0) at the top of the function and then I tried it right above the packet initialization, none worked. I also tried the PointsPacket.points in PythonNetworkStreamPhaseLoading.cpp and then I tried again with PointsChange.point_XX from the new packet you showed me, where PointsChange = TPacketGCPointChange PointsChange. None of these worked.

  3. Have a look at questlua_affect.cpp->int aff_affect(lua_state * L)

    int affect_add(lua_State * L)
    {
    	if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3))
    	{
    		sys_err("invalid argument");
    		return 0;
    	}
    
    	CQuestManager & q = CQuestManager::instance();
    
    	BYTE applyOn = (BYTE) lua_tonumber(L, 1);
    
    	LPCHARACTER ch = q.GetCurrentCharacterPtr();
    
    	if (applyOn >= MAX_APPLY_NUM || applyOn < 1)
    	{
    		sys_err("apply is out of range : %d", applyOn);
    		return 0;
    	}
    
    	if (ch->FindAffect(AFFECT_QUEST_START_IDX, applyOn)) // 퀘스트로 인해 같은 곳에 효과가 걸려있으면 스킵
    		return 0;
    
    	long value = (long) lua_tonumber(L, 2);
    	long duration = (long) lua_tonumber(L, 3);
    
    	ch->AddAffect(AFFECT_QUEST_START_IDX, aApplyInfo[applyOn].bPointType, value, 0, duration, 0, false);
    
    	return 0;
    }


    where L1 = affect ID, L2 = affect value, L3 = duration.

    If you use /stun MyChar as a GM and you refresh player.affect  in the database you will see a new affect under the dwPID of the character stunned. The bApplyOn value is the id of the affect in the system (our case 210). If you want to check your affect ids, open the file affect.h  in game/src and search for the following:

    enum EAffectTypes
    {
    	AFFECT_NONE,
    
    	AFFECT_MOV_SPEED		= 200,
    	AFFECT_ATT_SPEED,
    	AFFECT_ATT_GRADE,
    	AFFECT_INVISIBILITY,
    	AFFECT_STR,
    	AFFECT_DEX,			// 205
    	AFFECT_CON,	
    	AFFECT_INT,	
    	AFFECT_FISH_MIND_PILL,
    
    	AFFECT_POISON,
    	AFFECT_STUN,		// 210
    	AFFECT_SLOW,
    	AFFECT_DUNGEON_READY,
    	AFFECT_DUNGEON_UNIQUE,
    ...

    As you can see, the ids start from 200 and go on. The AFFECT_STUN is the 10th in the row, which gives it the ID of 210. To be sure, open your affect.h and see your ID of AFFECT_STUN.

    I also saw that you changed the value (0->1). The value on this affect is not important as it can only be a true (exists on the database table) or false (not found in the database table)

    I hope this helps

  4. I'm gonna make this as simple as it gets.

    Run the following commands as root:

    #service mysql-server stop
    #cd /var/db
    #tar -zcvf mysql.tar.gz ./mysql
    #rm -rf ./mysql

    Reboot your machine. Once mysql-server service runs without a /var/db/mysql folder, it automatically creates it's own.

    Run these now:

    #service mysql-server stop
    #/usr/local/bin/mysqld_safe --skip-grant-tables &
    #mysql -u root mysql
    > INSERT INTO mysql.user (host, user, password, select_priv, insert_priv, update_priv, delete_priv, create_priv, drop_priv, reload_priv, shutdown_priv, process_priv, file_priv, grant_priv, references_priv, index_priv, alter_priv, show_db_priv, super_priv, create_tmp_table_priv, lock_tables_priv, execute_priv, repl_slave_priv, repl_client_priv, create_view_priv, show_view_priv, create_routine_priv, alter_routine_priv, create_user_priv, event_priv, trigger_priv, create_tablespace_priv) VALUES ('%', 'root', PASSWORD('your-remote-root-password'), 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y',);
    > flush privileges;
    > exit;
    #killall mysqld
    #service mysql-server start

    WARNING: I'm using Maria-DB, not MySQL, which I'm strongly suggesting to do as well. If not, your system may defer from mine. There for you should check the INSERT command and modify it according to your fields. To see your fields type the command below right before INSERT INTO

    SELECT * FROM mysql.user;

    If you run this and see that a root user with % host is already there, INSERT won't work. Instead, change the password like this:

    > UPDATE mysql.user SET password=PASSWORD('your-specified-password') WHERE user='root';

    and after that continue as displayed above.

    When this is done and you check that you can log in MySQL from Navicat (or your software) copy the tar file in /var/db into your local drive and open it. In your FTP software, go in the folder /var/db/mysql and drag & drop everything from your tar file EXCEPT mysql folder!!!

     All this until now will definately solve your mysql database problem. For the other databases, download other Serverfiles and replace the tables that cannot be fixed. But again, DO NOT TOUCH mysql FOLDER! You can download any SF you want (Fliege, Maxmi, Invoice), the databases should be the same.

    I hope this was helpful.

    Best regards

  5. There are 2 ways to do this.

    1) Direct quest edit

        PROS:

    • Quick, simple fix, anyone can do it
    • Bypasses immunity

        CONS:

    • Not as stable as 2nd way

    2) Source edit

        PROS:

    • Stable solution
    • Your very own lua function on stunning people

        CONS:

    • Does NOT bypass immunity (people with immunity attribute on their shield won't be affected by this)
    • More advanced implementation (recommended to back-up files, more advanced knowledge needed to edit according to your specifications)
    • Not tested (by me)

        This method will require you to compile a new QC file (add the new command in game/src/quest/quest_functions and then gmake -j20 in that directory)

    Direct quest edit solution

    In this example I will be using the item by vnum 31001

    stun_quest.lua:

    Spoiler
    
    quest stun_quest begin
        state start begin
    		when 31001.use begin
    			affect.add(210, 0, 2) -- Replace 2 with the duration of the affect you want to set (in seconds).
    		end
        end
    end

     

    Source edit solution

    Go to game/src and edit the following in questlua_affect.cpp:

    Spoiler

    Add this to the inclludes:

    
    #include battle.h

    Find this function:

    
    int affect_remove_all_collect( lua_State* L )
    {
    	...
    }

    Add below the closing curly brace:

    
    int affect_stun()
    {
    	CQuestManager & q = CQuestManager::instance();
      	LPCHARACTER ch = q.GetCurrentCharacterPtr();
      
      	ch->AddAffect(AFFECT_QUEST_START_IDX, AFFECT_STUN, 0, 0, 2, 0, false); // Edit 2 by your stun duration specification (in seconds)
    
      	return 0;
    }

    And of course find:

    
    { "get_apply_on",	affect_get_apply_on },

    and add this below:

    
    { "stun",	affect_stun },

    After that, you can use this in a quest like this:

    stun_quest.lua

    
    quest stun_quest begin
        state start begin
    		when 31001.use begin
    			affect.stun()
    		end
        end
    end

    REMEMBER: If you use the second way, you need to add affect.stun in quest_functions, both in SF/locale/country/quest and game/src/quest and recompile the QC in game/src/quest

    I hope you found this helpful. Again, the second way is not tested and could be bugged (but I don't think so)

    Best regards

    • Love 1
  6. 10 hours ago, OtherChoice said:

    The way you are trying to work with won't go anywhere, first, the point you are packing are in a wrong place, since the first time ComputePoints() is exectuted it will check whether your horse has higher stats than yours and then replace them if so and will however pack and send them to client. If you want to make sure you have your real stat points you need to create the packet inside CHARACTER::ComputePoints() function not in void CHARACTER::Disconnect. Next point is the packet you are using: since packet_point_change (HEADER_GC_CHARACTER_POINT_CHANGE) has already a defined behavior (it has 3 different behavior actually, in PhaseGame, PhaseSelect and PhaseLoading) i suggest you to avoid using it.
    Moreover if your goal is to always show base stats and hide horse bonuses, you can skip the whole client part and do in a simpler way.
    I will first explain how to get both base Stats and horse Stats.

      Hide contents

    I just tried the code i wrote and everything works fine. There's just a couple of thing i missed when answering

     

    Before TPacketGCBaseStats pack add this:

    ...
    PointChange(POINT_ST, 0);
    PointChange(POINT_DX, 0);
    PointChange(POINT_IQ, 0);
    PointChange(POINT_HT, 0);
    TPacketGCBaseStats pack;
    pack.header = HEADER_GC_BASE_STATS;
    ...

    then in PythonNetworkStreamPhaseGame.cpp:

    
    
    void CPythonNetworkStream::GamePhase()
    {
    	...
    	switch (header)
    		{
    			case HEADER_GC_OBSERVER_ADD:
    				ret = RecvObserverAddPacket();
    				break;
        		....
    			case HEADER_GC_BASE_STATS:
    				ret = RecvBaseStatsPacket();
    					break;
      		}
    }
    
    then after
    bool CPythonNetworkStream::RecvPointChange()
    {
      ...
    }
    add
    bool CPythonNetworkStream::RecvBaseStatsPacket()
    {
      TPacketGCBaseStats pack;
      if (!Recv(sizeof(TPacketGCBaseStats), &pack))
    	{
    		Tracen("Recv Base Stats Packet Error");
    		return false;
    	}
      //here you can do whatever you like with those number, to make sure you everything runs smoothly you can try this
      TraceError("Point_ST = %d, Point_DX =  %d, Point_HT = %d, Point_IQ = %d \n", pack.point_ST, pack.point_DX, pack.POINT_HT, pack.point_IQ);
      // so you can run your client then check syserr.txt and look at the output
    }
    
    and in PythonNetworkStream.h after
      bool RecvPointChange();
    add
      bool RecvBaseStatsPacket();

     

    ____________________________________________________________________________________________________

    If you want to totally hide horse stats you can do something like this:

      Reveal hidden contents

     

    in char.cpp change void CHARACTER::PointChange(BYTE type, int amount, bool bAmount, bool bBroadcast) to 

    void CHARACTER::PointChange(BYTE type, int amount, int baseAmount=0, bool bAmount, bool bBroadcast) and change the definition in char.h as well (without =0 in the header file)

    then at the end of the function you will find 

    
    
    if (GetDesc())
    	{
    		struct packet_point_change pack;
    
    		pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
    		pack.dwVID = m_vid;
    		pack.type = type;
    		pack.value = val;
    
    		if (bAmount)
    			pack.amount = amount;
    		else
    			pack.amount = 0;
    
    		if (!bBroadcast)
    			GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
    		else
    			PacketAround(&pack, sizeof(pack));
    	}
    }
    
    and change it like this
    
    if (GetDesc())
    	{
    		struct packet_point_change pack;
    
    		pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
    		pack.dwVID = m_vid;
    		pack.type = type;
    		pack.value = baseAmount ? baseAmount : val;
    
    		if (bAmount)
    			pack.amount = amount;
    		else
    			pack.amount = 0;
    
    		if (!bBroadcast)
    			GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
    		else
    			PacketAround(&pack, sizeof(pack));
    	}
    }
    
    
    Lastly  you need to edit char.cpp void CHARACTER::ComputePoints()
    find
    if (GetMountVnum()) 
    		{
    			if (GetHorseST() > GetPoint(POINT_ST))
    				PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
    
    			if (GetHorseDX() > GetPoint(POINT_DX))
    				PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));
    
    			if (GetHorseHT() > GetPoint(POINT_HT))
    				PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
    
    			if (GetHorseIQ() > GetPoint(POINT_IQ))
    				PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));
    		}
    
    and change it like this
    
    if (GetMountVnum()) 
    		{
    			if (GetHorseST() > GetPoint(POINT_ST))
    				PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST), GetPoint(POINT_ST));
    
    			if (GetHorseDX() > GetPoint(POINT_DX))
    				PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX), GetPoint(POINT_DX));
    
    			if (GetHorseHT() > GetPoint(POINT_HT))
    				PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT), GetPoint(POINT_HT));
    
    			if (GetHorseIQ() > GetPoint(POINT_IQ))
    				PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ), GetPoint(POINT_IQ));
    		}

     

     

    Done.

    Thank you so much for these information they were very helpful, but my goal is not to hide the horse stats in general, just in character select. I'm trying to hide them the same way item attributes are hidden: hidden in character select, showing everywhere else.

    What I did with the client and the packet set in Disconnect() came from this post:

     

  7. 2 hours ago, OtherChoice said:

    In Server source game/char.cpp in ComputePoints() 

    
    if (IsPC())
    	{
    		if (GetMountVnum()) 
    		{
    			if (GetHorseST() > GetPoint(POINT_ST))
    				PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
    
    			if (GetHorseDX() > GetPoint(POINT_DX))
    				PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));
    
    			if (GetHorseHT() > GetPoint(POINT_HT))
    				PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
    
    			if (GetHorseIQ() > GetPoint(POINT_IQ))
    				PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));
    		}
    
    	}

    This way horse stats will override lower stats. Those numbers are then packeted and sent to client. You could create a new packet sending base stats to client. To recive base stats from game use game/char.cpp int CHARACTER::GetPoint(type) where type is POINT_ST, POINT_DX, POINT_HT, POINT_IQ example:

    
    game/packet.h && UserInterface/packet.h
      
      enum
    {
    	HEADER_CG_HANDSHAKE				= 0xdc,
      ....
        HEADER_GC_BASE_STATS			= (your_value),
      
      typedef struct base_stats
    {
    	BYTE	header;
    	DWORD	point_ST;
    	DWORD	point_DX;
    	DWORD	point_HT;
    	DWORD	point_IQ;
    } TPacketGCBaseStats;

     then send this packet from somewhere:
     

    
    if (IsPC())
    	{
    		if (GetMountVnum()) 
    		{
    			TPacketGCBaseStats pack;
    			pack.header = HEADER_GC_BASE_STATS;
    			pack.point_ST = GetPoint(POINT_ST);
    			pack.point_DX = GetPoint(POINT_DX);
    			pack.point_HT = GetPoint(POINT_HT);
    			pack.point_IQ = GetPoint(POINT_IQ);
    			ch->GetDesc()->Packet(&pack, sizeof(pack));
    			if (GetHorseST() > GetPoint(POINT_ST))
    				PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
    
    			if (GetHorseDX() > GetPoint(POINT_DX))
    				PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));
    
    			if (GetHorseHT() > GetPoint(POINT_HT))
    				PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
    
    			if (GetHorseIQ() > GetPoint(POINT_IQ))
    				PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));
    		}

    And then recive it from client and do whatever you like with those.

    REMEMBER to declare both the packet header and the typedef struct inside packet.h of client binary as well.

    I tested it and it didn't quite work. So I already had a packet in char.cpp->void CHARACTER:: Disconnect(const char * c_pszReason) and I changed it like this:

    packet_point_change pack;
    pack.header = HEADER_GC_CHARACTER_POINT_CHANGE;
    pack.dwVID = m_vid;
    pack.type = POINT_PLAYTIME;
    pack.value = GetRealPoint(POINT_PLAYTIME) + (get_dword_time() - m_dwPlayStartTime) / 60000;
    pack.amount = 0;
    pack.point_ST = GetPoint(POINT_ST);
    pack.point_DX = GetPoint(POINT_DX);
    pack.point_HT = GetPoint(POINT_HT);
    pack.point_IQ = GetPoint(POINT_IQ);
    GetDesc()->Packet(&pack, sizeof(struct packet_point_change));
    
    if (GetHorseST() > GetPoint(POINT_ST))
    	PointChange(POINT_ST, GetHorseST() - GetPoint(POINT_ST));
    
    if (GetHorseDX() > GetPoint(POINT_DX))
    	PointChange(POINT_DX, GetHorseDX() - GetPoint(POINT_DX));
    
    if (GetHorseHT() > GetPoint(POINT_HT))
    	PointChange(POINT_HT, GetHorseHT() - GetPoint(POINT_HT));
    
    if (GetHorseIQ() > GetPoint(POINT_IQ))
    	PointChange(POINT_IQ, GetHorseIQ() - GetPoint(POINT_IQ));

    and then I changed this in UserInterface/PythonNetworkStreamPhaseLogin.cpp:

    for (DWORD i = 0; i < POINT_MAX_NUM; ++i)
    {
    	CPythonPlayer::Instance().SetStatus(i, PointsPacket.points[i]);
    	TPacketGCPointChange PointsPacketChange;
    
    	if (i == POINT_LEVEL)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byLevel = PointsPacket.points[i];
    	else if (i == POINT_ST)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byST = PointsPacketChange.point_ST;
    	else if (i == POINT_HT)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byHT = PointsPacketChange.point_HT;
    	else if (i == POINT_DX)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byDX = PointsPacketChange.point_DX;
    	else if (i == POINT_IQ)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byIQ = PointsPacketChange.point_IQ;
    }

    and I edited the packet.h files of course.

    The result was that not only the problem continued to exist, but once the client starts, if the character logs out once with horse stats they are displayed each time they do, even if they don't ride a horse the next time! And not only that, when the character rides a horse in game, the HT, IQ, ST and DEX get updated but HP and SP are not updated!

  8. So if I did something like this in questlua_pc.cpp->int pc_give_or_drop_item(lua_State* L)

    LogManager::instance().QuestRewardLog(pPC->GetCurrentQuestName().c_str(), ch->GetPlayerID(), ch->GetLevel(), dwVnum, icount);
    
    if (dwVnum->IsStackable() && !IS_SET(dwVnum->GetAntiFlag(), ITEM_ANTIFLAG_STACK))
    {
    	BYTE bCount = dwVnum->GetCount();
    	for (int i = 0; i < INVENTORY_MAX_NUM; ++i)
        {
        	LPITEM item2 = ch->GetInventoryItem(i);
    
            if (!item2)
            	continue;
          
            if (item2->GetVnum() == item->GetVnum())
            {
            	int j;
    
                for (j = 0; j < ITEM_SOCKET_MAX_NUM; ++j)
                	if (item2->GetSocket(j) != dwVnum->GetSocket(j))
                    	break;
    
    		if (j != ITEM_SOCKET_MAX_NUM)
            	continue;
    
            BYTE bCount2 = std::min(200 - item2->GetCount(), bCount);
            bCount -= bCount2;
    
            item2->SetCount(item2->GetCount() + bCount2);
    
            if (bCount == 0)
            {
            	LPITEM item = ch->AutoGiveItem(dwVnum, icount);
            }
        }
    }

    would that work or I'm missing something?

    • Sad 1
  9. On 12/6/2015 at 1:22 PM, Ken said:

    At first, LoadMonsterAreInfo's first error is coming from regen.txt. If you don't have this file in your pack. System will give this error. As galet said, you're just hidding that.

     

    
    	char c_szFileName[256];
    	sprintf(c_szFileName, "%s\\regen.txt", GetMapDataDirectory().c_str());
    	
    	LPCVOID pModelData;
    	CMappedFile File;
    	
    	if (!CEterPackManager::Instance().Get(File, c_szFileName, &pModelData))
    	{
    		//TraceError(" CMapOutdoorAccessor::LoadMonsterAreaInfo Load File %s ERROR", c_szFileName);
    		return false;
    	}

     The code is already explain itself.

    If you want to fix it, here is a fix for you.

     

    
    bool CMapOutdoor::LoadMonsterAreaInfo()
    {
    	RemoveAllMonsterInfo(); // Remove All Monster Info
    	
    	char szFileName[256];
    	_snprintf(szFileName, sizeof(szFileName), "%s\\monsterareainfo.txt", GetMapDataDirectory().c_str());
    	
    	LPCVOID pModelData;
    	CMappedFile File;
    	
    	if (!CEterPackManager::Instance().Get(File, szFileName, &pModelData)) return false;
    	
    	CMemoryTextFileLoader textFileLoader;
    	CTokenVector stTokenVector;
    	
    	textFileLoader.Bind(File.size(), pModelData);
    	
    	for (DWORD i = 0; i < textFileLoader.GetLineCount(); ++i)
    	{
    		if (!textFileLoader.SplitLine(i, &stTokenVector))
    			continue;
    		
    		stl_lowers(stTokenVector[0]);
    		
    		// Start to read MonsterAreaInfo.txt
    		if (0 == stTokenVector[0].compare("m") || 0 == stTokenVector[0].compare("g"))
    		{
    			if (stTokenVector.size() < 11)
    			{
    				TraceError("CMapOutdoorAccessor::LoadMonsterAreaInfo Get MonsterInfo File Format ERROR! continue....");
    				continue;
    			}
    
    			CMonsterAreaInfo::EMonsterAreaInfoType eMonsterAreaInfoType;
    			if (0 == stTokenVector[0].compare("m"))
    			{
    				eMonsterAreaInfoType = CMonsterAreaInfo::MONSTERAREAINFOTYPE_MONSTER;
    			}
    			else if (0 == stTokenVector[0].compare("g"))
    			{
    				eMonsterAreaInfoType = CMonsterAreaInfo::MONSTERAREAINFOTYPE_GROUP;
    			}
    			else
    			{
    				TraceError("CMapOutdoorAccessor::LoadMonsterAreaInfo Get MonsterInfo Data ERROR! continue....");
    				continue;
    			}
    			
    			const std::string & c_rstrOriginX	= stTokenVector[1].c_str();
    			const std::string & c_rstrOriginY	= stTokenVector[2].c_str();
    			const std::string & c_rstrSizeX		= stTokenVector[3].c_str();
    			const std::string & c_rstrSizeY		= stTokenVector[4].c_str();
    			const std::string & c_rstrZ			= stTokenVector[5].c_str();
    			const std::string & c_rstrDir		= stTokenVector[6].c_str();
    			const std::string & c_rstrTime		= stTokenVector[7].c_str();
    			const std::string & c_rstrPercent	= stTokenVector[8].c_str();
    			const std::string & c_rstrCount		= stTokenVector[9].c_str();
    			const std::string & c_rstrVID		= stTokenVector[10].c_str();
    			
    			long lOriginX, lOriginY, lSizeX, lSizeY, lZ, lTime, lPercent;
    			CMonsterAreaInfo::EMonsterDir eMonsterDir;
    			DWORD dwMonsterCount;
    			DWORD dwMonsterVID;
    			
    			lOriginX		= atol(c_rstrOriginX.c_str());
    			lOriginY		= atol(c_rstrOriginY.c_str());
    			lSizeX			= atol(c_rstrSizeX.c_str());
    			lSizeY			= atol(c_rstrSizeY.c_str());
    			lZ				= atol(c_rstrZ.c_str());
    			eMonsterDir		= (CMonsterAreaInfo::EMonsterDir) atoi(c_rstrDir.c_str());
    			lTime			= atol(c_rstrTime.c_str());
    			lPercent		= atol(c_rstrPercent.c_str());
    			dwMonsterCount	= (DWORD) atoi(c_rstrCount.c_str());
    			dwMonsterVID	= (DWORD) atoi(c_rstrVID.c_str());
    			
    			CMonsterAreaInfo * pMonsterAreaInfo = AddMonsterAreaInfo(lOriginX, lOriginY, lSizeX, lSizeY);
    			pMonsterAreaInfo->SetMonsterAreaInfoType(eMonsterAreaInfoType);
    			if (CMonsterAreaInfo::MONSTERAREAINFOTYPE_MONSTER == eMonsterAreaInfoType)
    				pMonsterAreaInfo->SetMonsterVID(dwMonsterVID);
    			else if (CMonsterAreaInfo::MONSTERAREAINFOTYPE_GROUP == eMonsterAreaInfoType)
    				pMonsterAreaInfo->SetMonsterGroupID(dwMonsterVID);
    			pMonsterAreaInfo->SetMonsterCount(dwMonsterCount);
    			pMonsterAreaInfo->SetMonsterDirection(eMonsterDir);
    		}		
    	}
    
    	return true;
    }

    The system cannot read the actual file. File name is MonsterAreaInfo.txt but system is trying to read regen.txt.

    If you want, you can change monsterareainfo.txt with regen.txt - result will be same.

    Kind Regards ~ Ken

    Epic save! Thanks!

    • Confused 1
  10. 17 minutes ago, PACI said:

    Probably the client sniffing around. That exact character (along with brackets and vertical bar) is used for parsing quests' strings sent by the server. At this point, you may have noticed that the client doesn't only receives whatever text a quest window has, but also an event type bound to that said text, being it formatting, window resizing, item previews and whatnot. 
    Those actions are read this way.

    This is what the server will send to the client, using the example you provided above:

    
    [QUESTION resume;0|1;Hi, how are you today;|2;Close]

    resume stands for, eh.. a suspended state, can be said I guess. Meaning that it requires the player to answer. Everything else are necessary data the binary will send to the python-side of your client in order to create the so well known: chat-event. In order words, the buttons.

    My suggestion is to jump into your PythonEventManager class for further info. The parser is in EterLib/parser.*


     

    Thank you so much for your reply. I searched in the files you mentioned and I found the check that handles this character in parser.cpp

    else if (c == ';')
    {
    	isValue = true;
    }

    I tried changing this to false but the result was not what I expected... I do not understand the most out of the code yet so I have to ask for a simple example. I thought maybe sending signal-character will do the trick. For example, I send ^ and write another check in this function that replaces

    ^[SPACE][END-OF-LINE]

    with the character. The only problem is that I don't know how to edit the string's characters so I'm gonna have to request an example.

    Again, thank you so much for the reply, it really helped a lot.

  11. I've noticed this one since the beginning but for some reason I've been waiting until now to ask.

    Let's say I'm writing a dialog with NPC X and when I click on it I want it to say:

        Hi, how are you today;

    So I'm writing the classic when XXXXXX.chat."Hi, how are you today;and I'm compiling but when I click in the game the ; is missing! So it just says:

        Hi, how are you today

    I haven't yet find what filters the character so I'm asking you. I've looked in questlua.cpp and questlua_global.cpp but didn't find anything.

    If anyone knows I'm also trying to find a way to increase the dialog width (after a line ends, there is just soooo much right padding!)

  12. I have those lines in my Client source

    PythonNetworkStreamPhaseLoading.cpp -> bool CPythonNetworkStream::__RecvPlayerPoints()

    for (DWORD i = 0; i < POINT_MAX_NUM; ++i)
    {
    	CPythonPlayer::Instance().SetStatus(i, PointsPacket.points[i]);
    	
        if (i == POINT_LEVEL)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byLevel = PointsPacket.points[i];
    	else if (i == POINT_ST)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byST = PointsPacket.points[i];
    	else if (i == POINT_HT)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byHT = PointsPacket.points[i];
    	else if (i == POINT_DX)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byDX = PointsPacket.points[i];
    	else if (i == POINT_IQ)
    		m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byIQ = PointsPacket.points[i];
    
    }

    PythonNetworkStreamPhaseGame.cpp -> bool CPythonNetworkStream::RecvPointChange()

    case POINT_PLAYTIME:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].dwPlayMinutes = PointChange.value;
    	break;
    case POINT_LEVEL:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byLevel = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    case POINT_ST:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byST = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    case POINT_DX:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byDX = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    case POINT_HT:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byHT = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;
    case POINT_IQ:
    	m_akSimplePlayerInfo[m_dwSelectedCharacterIndex].byIQ = PointChange.value;
    	__RefreshStatus();
    	__RefreshSkillWindow();
    	break;

    But there is a problem with this code. If I'm riding my horse, it returns the stats the character has while riding.

    For example if a character has INT: 6 and starts riding, the 6 will become 38.

    If this code executes while the character is riding the horse, POINT_IQ will return 38 instead of the real INT (6).

    Is there any way to get the real values?

×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.