KnightOnline icon indicating copy to clipboard operation
KnightOnline copied to clipboard

CUser::Regene() bugs

Open twostars opened this issue 3 months ago • 2 comments

CUser::Regene() (used on respawn) is a bit outdated and buggy. Ignoring the functionality that doesn't exist, the 2 main problems with it right now are that:

  1. The positioning logic is outdated and can fall through to not even finding a position (e.g. when dying in Moradon).
  2. Doesn't restore HP. This is apparently just commented out; it shouldn't be.

twostars avatar Oct 19 '25 18:10 twostars

  1. Position Logic. As I looked into it, the logic for moradon is just this
	float x = (float) (myrand(0, 400) / 100.0f);
	float z = (float) (myrand(0, 400) / 100.0f);

	if (x < 2.5f)
		x += 1.5f;

	if (z < 2.5f)
		z += 1.5f;

Do you have description how it should work for moradon?

  1. The HpChange is also commented out for BattleZone:
/*
				m_bResHpType = USER_STANDING;
				HpChange( m_iMaxHp );
				KickOutZoneUser();	// Go back to your own zone!
				return;
*/

How do we wanna go on with this one? As I remember it we should enable standing and hpchange, or?

stefannikolei avatar Dec 16 '25 13:12 stefannikolei

The official implementation for 1.298:

void __thiscall CUser::Regene(CUser *this, char *pBuf, int magicid)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

  memset(logstr, 0, sizeof(logstr));
  if ( this->m_bResHpType == 3 )
  {
    if ( this->m_pUserData->m_sHp > 0 )
      CUser::HpChange(this, -this->m_iMaxHp, 0, 0);
    if ( this->m_PetSystem.m_sPetID > 0 )
    {
      memset(ai_send_buff, 0, sizeof(ai_send_buff));
      regene_index = this->m_Sid;
      ai_send_buff[0] = AG_USER_PET_SYSTEM;
      ai_send_buff[1] = 1;
      ai_send_buff[2] = 1;
      ai_send_buff[3] = PET_USERGUARD_COMMAND_DESTROY;
      memcpy(&ai_send_buff[4], &regene_index, 2u);
      LOWORD(v4) = this->m_PetSystem.m_sPetID;
      regene_index = v4;
      memcpy(&ai_send_buff[6], &regene_index, 2u);
      CEbenezerDlg::Send_AIServer(this->m_pMain, this->m_pUserData->m_bZone, ai_send_buff, 8u);
      this->m_PetSystem.m_sPetID = -1;
    }
    CUser::InitType3(this);
    CUser::InitType4(this);
    CUser::InitType6(this);
    CUser::InitType9(this, 1);
    CUser::InitType9(this, 2);
    CUser::InitType9(this, 3);
    CUser::UserInOut(this, INOUT_OUT);
    CTime::GetTickCount(a1);
    regene_type = *pBuf;
    regene_type = *pBuf;
    if ( regene_type != 1 )
    {
      if ( regene_type == 2 )
      {
        m_pUserData = this->m_pUserData;
        magicid = 490041;
        if ( (unsigned __int8)CUser::ItemCountChange(this, 379006000, 1, 3 * m_pUserData->m_bLevel, 0) < 2u )
        {
          memset(logstr, 0, sizeof(logstr));
          _snprintf(
            logstr,
            256u,
            "$$ UserRegene Fail 2 - acid=%s, charid=%s, magicid=%d, type=%d, level=%d, zone=%d $$",
            this->m_pUserData->m_Accountid,
            this->m_pUserData,
            490041,
            this->m_bResHpType,
            this->m_pUserData->m_bLevel,
            this->m_pUserData->m_bZone);
          goto write_event_log;
        }
        if ( this->m_pUserData->m_bLevel <= 5u )
          return;
      }
      else
      {
        regene_type = 1;
      }
    }
    if ( !CSTLMap<_HOME_INFO>::GetData(&this->m_pMain->m_HomeArray, this->m_pUserData->m_bNation) )
    {
      memset(logstr, 0, sizeof(logstr));
      _snprintf(
        logstr,
        256u,
        "$$ UserRegene Fail 3 - acid=%s, charid=%s, magicid=%d, type=%d, level=%d, zone=%d $$",
        this->m_pUserData->m_Accountid,
        this->m_pUserData,
        magicid,
        this->m_bResHpType,
        this->m_pUserData->m_bLevel,
        this->m_pUserData->m_bZone);
      goto write_event_log;
    }
    memset(send_buff, 0, sizeof(send_buff));
    m_iZoneIndex = this->m_iZoneIndex;
    if ( m_iZoneIndex < 0
      || ((Myfirst = this->m_pMain->m_ZoneArray._Myfirst) != 0 ? (v9 = this->m_pMain->m_ZoneArray._Mylast - Myfirst) : (v9 = 0),
          m_iZoneIndex >= v9) )
    {
      memset(logstr, 0, sizeof(logstr));
      _snprintf(
        logstr,
        256u,
        "$$ UserRegene Fail 4 - acid=%s, charid=%s, magicid=%d, type=%d, level=%d, zone=%d $$",
        this->m_pUserData->m_Accountid,
        this->m_pUserData,
        magicid,
        this->m_bResHpType,
        this->m_pUserData->m_bLevel,
        this->m_pUserData->m_bZone);
      goto write_event_log;
    }
    *(float *)&regene_index = COERCE_FLOAT(myrand(0, 400));
    *(float *)&a1[1] = (double)regene_index * 0.0099999998;
    *(float *)&regene_index = (double)myrand(0, 400) * 0.0099999998;
    if ( v11 )
      *(float *)&a1[1] = *(float *)&a1[1] + 1.5;
    if ( *(float *)&regene_index < 2.5 )
      *(float *)&regene_index = *(float *)&regene_index + 1.5;
    EnterCriticalSection(&g_region_critical);
    pEvent = CSTLMap<_OBJECT_EVENT>::GetData(
               &this->m_pMain->m_ZoneArray._Myfirst[this->m_iZoneIndex]->m_ObjectEventArray,
               this->m_pUserData->m_sBind);
    LeaveCriticalSection(&g_region_critical);
    if ( magicid )
    {
after_magicid_check:
      m_pUserData = this->m_pUserData;
      send_buff[0] = WIZ_REGENE;
      pEvent = (_OBJECT_EVENT *)(10 * (__int64)m_pUserData->m_curx);
      memcpy(&send_buff[1], &pEvent, 2u);
      pEvent = (_OBJECT_EVENT *)(10 * (__int64)this->m_pUserData->m_curz);
      memcpy(&send_buff[3], &pEvent, 2u);
      pEvent = (_OBJECT_EVENT *)(10 * (__int64)this->m_pUserData->m_cury);
      memcpy(&send_buff[5], &pEvent, 2u);
      CIOCPSocket2::Send(this, send_buff, 7, 0);
      if ( magicid <= 0 )
      {
        m_iMaxHp = this->m_iMaxHp;
        this->m_bResHpType = 1;
        CUser::HpChange(this, m_iMaxHp, 0, 0);
        this->m_bRegeneType = 0;
      }
      else
      {
        pType = CSTLMap<_MAGIC_TYPE5>::GetData(&this->m_pMain->m_Magictype5Array.__vftable, magicid);
        if ( !pType )
        {
          memset(logstr, 0, sizeof(logstr));
          _snprintf(
            logstr,
            256u,
            "$$ UserRegene Fail 5 - acid=%s, charid=%s, magicid=%d, type=%d, level=%d, zone=%d $$",
            this->m_pUserData->m_Accountid,
            this->m_pUserData,
            magicid,
            this->m_bResHpType,
            this->m_pUserData->m_bLevel,
            this->m_pUserData->m_bZone);
          goto write_event_log;
        }
        v36 = this->m_iMaxHp;
        this->m_bResHpType = 1;
        CUser::HpChange(this, v36, 0, 0);
        CUser::MSpChange(this, -this->m_iMaxMp);
        if ( regene_type == 1 )
        {
          CUser::ExpChange(this, this->m_iLostExp * pType->bExpRecover / 100, 0, 0);
          CUser::DeathLogToAgent(this, this->m_Sid, this->m_Sid, this->m_iLostExp * pType->bExpRecover / 100);
        }
        this->m_bRegeneType = 1;
      }
      v31 = TimeGet();
      v32 = this->m_pUserData;
      this->m_sWhoKilledMe = -1;
      this->m_fLastRegeneTime = v31;
      this->m_iLostExp = 0;
      v32->m_bCity = 0;
      if ( this->m_bAbnormalType != 4 )
      {
        memset(send_buff, 0, sizeof(send_buff));
        magicid = this->m_Sid;
        send_buff[0] = AG_USER_REGENE;
        memcpy(&send_buff[1], &magicid, 2u);
        v33 = this->m_pUserData;
        LOWORD(v33) = v33->m_sHp;
        magicid = (int)v33;
        memcpy(&send_buff[3], &magicid, 2u);
        CEbenezerDlg::Send_AIServer(this->m_pMain, this->m_pUserData->m_bZone, send_buff, 5u);
      }
      memset(send_buff, 0, sizeof(send_buff));
      wsprintfA(
        send_buff,
        "<------ User Regene ,, nid=%d, name=%s, type=%d ******",
        this->m_Sid,
        this->m_pUserData->m_id,
        this->m_bResHpType);
      memset(send_buff, 0, sizeof(send_buff));
      v34 = this->m_pUserData;
      this->m_RegionX = (__int64)(v34->m_curx * 0.020833334);
      this->m_RegionZ = (__int64)(v34->m_curz * 0.020833334);
      CUser::UserInOut(this, INOUT_RESPAWN);
      CEbenezerDlg::UserInOutForMe(this->m_pMain, this);
      CEbenezerDlg::NpcInOutForMe(this->m_pMain, this);
      CUser::BlinkStart(this);
      CUser::ItemMallMagicRecast(this, 1);
      CUser::SetSlotItemValue(this);
      CUser::SetUserAbility(this, 0);
      CUser::Send2AI_UserUpdateInfo(this);
      CUser::SendMonsterFriendlyFlagToAI(this, 0);
      memset(send_buff, 0, sizeof(send_buff));
      CUser::StateChangeServerDirect(this, STATE_CHANGE_STEALTH, this->m_byInvisibilityType);
      memset(send_buff, 0, sizeof(send_buff));
      strcpy(send_buff, "`");
      magicid = 0;
      memcpy(&send_buff[2], &magicid, 2u);
      CIOCPSocket2::Send(this, send_buff, 4, 0);
      if ( this->m_sPartyIndex != -1 )
      {
        if ( this->m_bType3Flag )
          goto LABEL_89;
        CUser::SendPartyUserStatusChange(this, USER_STATUS_DOT, USER_STATUS_CURE);
      }
      if ( !this->m_bType3Flag )
        CUser::SendUserStatusChange(this, USER_STATUS_DOT, USER_STATUS_CURE);
LABEL_89:
      if ( this->m_sPartyIndex != -1 )
      {
        if ( this->m_bType4Flag )
        {
LABEL_94:
          v35 = this->m_pUserData;
          this->m_fWill_x = v35->m_curx;
          this->m_fWill_z = v35->m_curz;
          this->m_fWill_y = v35->m_cury;
          return;
        }
        CUser::SendPartyUserStatusChange(this, USER_STATUS_POISON, USER_STATUS_CURE);
        CUser::SendPartyUserStatusChange(this, USER_STATUS_SPEED, USER_STATUS_CURE);
      }
      if ( !this->m_bType4Flag )
      {
        CUser::SendUserStatusChange(this, USER_STATUS_POISON, USER_STATUS_CURE);
        CUser::SendUserStatusChange(this, USER_STATUS_SPEED, USER_STATUS_CURE);
      }
      goto LABEL_94;
    }
    m_pUserData = this->m_pUserData;
    bZone = m_pUserData->m_bZone;
    v12 = bZone;
    if ( bZone == ZONE_ESLANT_KARUS || bZone == ZONE_ESLANT_ELMORAD )
    {
      if ( m_pUserData->m_bLevel >= 60u )
        goto skip_to_bind_point_check;
      goto kickout_and_skip_to_bind_point_check;
    }
    if ( bZone == ZONE_FRONTIER )
    {
      if ( this->m_pMain->m_byNationID != 3 )
      {
        if ( m_pUserData->m_bLevel < 30u )
          CUser::KickOutZoneUser(this, 1, 0);
        if ( this->m_pMain->m_byNationID != 1 )
          goto skip_to_bind_point_check;
        v14 = this->m_pUserData->m_iLoyalty <= 0;
LABEL_41:
        if ( v14 )
          goto LABEL_42;
skip_to_bind_point_check:
        v15 = pEvent;
        if ( pEvent && pEvent->byLife == 1 )
        {
          v17 = *(float *)&a1[1] + pEvent->fPosX;
          v18 = this->m_pUserData;
          this->m_fWill_x = v17;
          v18->m_curx = v17;
          v16 = *(float *)&regene_index + v15->fPosZ;
          this->m_fWill_z = v16;
        }
        else
        {
          v19 = this->m_pUserData;
          m_bZone = v19->m_bZone;
          if ( m_bZone == ZONE_DELOS )
          {
            p_m_KnightsSiegeWar = &this->m_pMain->m_KnightsSiegeWar;
            *(float *)&regene_index = 0.0;
            CKnightsSiegeWar::GetRegenePosition(p_m_KnightsSiegeWar, &vPos, this, &regene_index, 0);
            if ( *(float *)&regene_index == 0.0 )
            {
              CUser::KickOutZoneUser(this, 1, 21);
            }
            else
            {
              this->m_pUserData->m_curx = vPos.x;
              this->m_pUserData->m_curz = vPos.z;
            }
LABEL_72:
            v28 = this->m_pUserData->m_bZone;
            if ( v28 >= ZONE_PRIVATE_ARENA_1 && v28 <= ZONE_PRIVATE_ARENA_6 )
            {
              m_Sid = this->m_Sid;
              this->m_byRoomParticipationType = 2;
              CUser::PartyRemove(this, m_Sid);
            }
            goto after_magicid_check;
          }
          m_pMain = this->m_pMain;
          if ( m_pMain->m_byBattleOpen != NATION_BATTLE
            || (!m_pMain->m_byKarusOpenFlag || v19->m_bNation != 1)
            && (!m_pMain->m_byElmoradOpenFlag || v19->m_bNation != 2)
            || m_bZone >= (ZONE_CASTLE_ELMORAD|ZONE_CASTLE_LUFERSON) )
          {
            goto LABEL_70;
          }
          v24 = myrand(0, 9000);
          v25 = this->m_pUserData;
          *(float *)&regene_index = 0.0;
          ZoneIndex = CEbenezerDlg::GetZoneIndex(this->m_pMain, v25->m_bNation);
          if ( v24 >= 0 )
          {
            if ( v24 >= 3000 )
            {
              if ( v24 >= 6000 )
              {
                if ( v24 < 9001 )
                  regene_index = 2;
              }
              else
              {
                regene_index = 1;
              }
            }
            else
            {
              *(float *)&regene_index = 0.0;
            }
          }
          Data = CSTLMap<_REGENE_EVENT>::GetData(
                   &this->m_pMain->m_ZoneArray._Myfirst[ZoneIndex]->m_ObjectRegeneArray,
                   regene_index);
          v27 = Data;
          if ( Data )
          {
            pEvent = (_OBJECT_EVENT *)myrand(0, (__int64)Data->fRegeneAreaX);
            this->m_pUserData->m_curx = (double)(int)pEvent + v27->fRegenePosX;
            pEvent = (_OBJECT_EVENT *)myrand(0, (__int64)v27->fRegeneAreaZ);
            v16 = (double)(int)pEvent + v27->fRegenePosZ;
          }
          else
          {
LABEL_70:
            pEvent = (_OBJECT_EVENT *)CUser::GetStartPosition((int)this, 1);
            this->m_pUserData->m_curx = (float)(int)pEvent;
            pEvent = (_OBJECT_EVENT *)CUser::GetStartPosition((int)this, 2);
            v16 = (double)(int)pEvent;
          }
        }
        this->m_pUserData->m_curz = v16;
        this->m_pUserData->m_cury = 0.0;
        goto LABEL_72;
      }
    }
    else
    {
      v12 = bZone;
      if ( bZone / 100 != 1 && (bZone == m_pUserData->m_bNation || bZone >= (ZONE_CASTLE_ELMORAD|ZONE_CASTLE_LUFERSON)) )
      {
LABEL_43:
        if ( v12 != ZONE_FORGOTTEN_TEMPLE )
          goto skip_to_bind_point_check;
kickout_and_skip_to_bind_point_check:
        CUser::KickOutZoneUser(this, 1, 21);
        goto skip_to_bind_point_check;
      }
    }
    if ( this->m_pMain->m_byNationID == 3 )
    {
      if ( m_pUserData->m_bLevel < 30u )
      {
LABEL_42:
        CUser::KickOutZoneUser(this, 1, 0);
        goto skip_to_bind_point_check;
      }
      v14 = m_pUserData->m_iLoyalty <= 0;
      goto LABEL_41;
    }
    goto LABEL_43;
  }
  memset(logstr, 0, sizeof(logstr));
  _snprintf(
    logstr,
    256u,
    "$$ UserRegene Fail 1 - acid=%s, charid=%s, magicid=%d, type=%d, zone=%d $$",
    this->m_pUserData->m_Accountid,
    this->m_pUserData->m_id,
    magicid,
    this->m_bResHpType,
    this->m_pUserData->m_bZone);
write_event_log:
  CEbenezerDlg::WriteEventLog(this->m_pMain, logstr);
}

twostars avatar Dec 16 '25 13:12 twostars