#include "stdafx.h" #include "stdafx.h" #include "SKMoveControler.h" #include "SGameAvatarEx.h" #include "KPrimitiveMesh.h" #include "SGameCamera.h" #include "SGameSystem.h" #include "SCommandSystem.h" #include "SGameMessage.h" #include "KViewport.h" #include "SGame.h" #include "TerrainMapEngine.h" #include "SGameWorld.h" #include "ErrorCode/ErrorCode.h" #include "SGameOption.h" #include "KUIControlEdit.h" #include "SGameSystem.h" #include "Arena\\ArenaSystem.h" #include "SGameWorldKeymapping.h" #include "SUIDisplayInfo.h" #include "SGameLocalPlayer.h" extern SGameSystem* g_pCurrentGameSystem; extern bool g_bActiveRender; //#define UPDATE_TIME_RATE 0.8f // ONE_STEP_KMOVE_LENGTH를 이동했을 때, 걸리는 시간의 비율, 주기적으로 보내는 시간 //#define LAST_MOVE_LEN_RATE 0.3f // 마지막 이동 패킷을 보낼 때, 이동 거리 float st_move_length= 48.0f; // 한번 클릭으로 갈 수 있는 최소 거리, 적당한 거리를 임의로 정했다. float st_stop_rate = 0.3f; float st_update_rate= 0.8f; int st_interval = 30; SKMoveControler::SKMoveControler() { m_commandSystem = NULL; m_gameSystem = NULL; m_gameCamera = NULL; m_arObject = NULL; m_arTime = 0; m_automove = FALSE; m_servermove = 0; } SKMoveControler::~SKMoveControler() { } void SKMoveControler::initialize(SCommandSystem* commandSystem, SGameSystem* gameSystem, SGameCamera* gameCamera, SGameWorld* gameWorld, ArcadiaClient* arObject) { m_commandSystem = commandSystem; m_gameSystem = gameSystem; m_gameCamera = gameCamera; m_gameWorld = gameWorld; m_arObject = arObject; } // 이동 또는 정지 //if ( cur_state == SObjectState::STATE_SKILL_END) //{ player->m_pStateVM->StopCurrentState(); //} //if (cur_state==SObjectState::STATE_SHOOTING) //{ stop_move(player,false); // player->ReqCancelAction(); // player->m_pStateVM->StopCurrentState(); // return; // } K3DVector SKMoveControler::get_local_pos() { K3DVector pos = m_sent_pos; SGameAvatarEx* player = m_gameSystem->GetLocalPlayer(); if (!player) return pos; pos = player->getMyPos(); pos.x = floor(pos.x+0.5f); pos.y = floor(pos.y+0.5f); return pos; } void SKMoveControler::process(int gameRef) { if (SGame::STAGE_ING != gameRef) return ; SGameAvatarEx* player = m_gameSystem->GetLocalPlayer(); if (!player) return; SGameLocalPlayer* localPlayer = dynamicCast(g_pCurrentGameSystem->GetLocalPlayer()); if (localPlayer) { ArPosition _target; _target.x = 0; _target.y = 0; _target.z = 0; localPlayer->ReqGameObjectMove( 0, _target, &_target, 1, true ); if (_target.x) return; } // 1.키보드 방향키를 구한다. int base_key_status = MOVE_NONE; // AziaMafia clavier move if (GetAsyncKeyState(GetGameKeymapping().GetKeymapping( HOTKEYCODE::HMOVE_UP ).wParam) & 0x8000) base_key_status |= MOVE_UP; if (GetAsyncKeyState(GetGameKeymapping().GetKeymapping( HOTKEYCODE::HMOVE_DOWN ).wParam) & 0x8000) base_key_status |= MOVE_DOWN; if (GetAsyncKeyState(GetGameKeymapping().GetKeymapping( HOTKEYCODE::HMOVE_LEFT ).wParam) & 0x8000) base_key_status |= MOVE_LEFT; if (GetAsyncKeyState(GetGameKeymapping().GetKeymapping( HOTKEYCODE::HMOVE_RIGHT).wParam) & 0x8000) base_key_status |= MOVE_RIGHT; int curr_key_status = base_key_status; // 자동이동중일경우 화면방향(UP)키 세팅 int key_by_automove = FALSE; if (m_automove && curr_key_status==0) { curr_key_status = MOVE_UP; key_by_automove = TRUE; } // 이동불가 상태 체크 static const int lock_state = SGameAvatarEx::FLAG_STATE::FLAG_STATE_PETRIFY | SGameAvatarEx::FLAG_STATE::FLAG_STATE_FEAR | SGameAvatarEx::FLAG_STATE::FLAG_STATE_SLEEP | SGameAvatarEx::FLAG_STATE::FLAG_STATE_STUN | SGameAvatarEx::FLAG_STATE::FLAG_STATE_FROZEN | SGameAvatarEx::FLAG_STATE::FLAG_STATE_HOLD; SObjectState::ID cur_state = player->m_pStateVM->GetCurrentState(); int cur_ani = player->GetCurrAnimationID(); if (g_pCurrentGameSystem == NULL ) return; if (m_gameWorld->GetGameManager()->IsShowLoadingWnd() ) return; if (key_by_automove==FALSE && g_bActiveRender==FALSE) return; if (m_servermove) { int moving = player->IsMoving(); if (moving==FALSE) { m_servermove = FALSE; } else { return; } } int cur_tick = timeGetTime(); if (KUIControlEdit::IsFocusEdit() ) curr_key_status = MOVE_NONE; else if ( m_gameWorld->GetGameManager()->IsShow(SIMSG_TOGGLE_UIWINDOW::UIWINDOW_STORE_HOST) ) curr_key_status = MOVE_NONE; else // 노점이 열려있는가?? if (GetGameOption().GetOptData().nEnterChat == false ) curr_key_status = MOVE_NONE; else if (m_gameWorld->GetGameManager()->GetWndManager()->IsOpenModalWnd() == true ) curr_key_status = MOVE_NONE; else if (player->GetStateFlag() & lock_state ) curr_key_status = MOVE_NONE; else if (player->IsDead()) curr_key_status = MOVE_NONE; else if (cur_state == SObjectState::STATE_FIRE || cur_state == SObjectState::STATE_ATTACK) curr_key_status = MOVE_NONE; else if (cur_state == SObjectState::STATE_CAST || cur_state == SObjectState::STATE_AIMING) curr_key_status = MOVE_NONE; else if (cur_state == SObjectState::STATE_SHOOTING && cur_ani != ANI_BOW_ATTACK01_C ) curr_key_status = MOVE_NONE; else if ( last_skill_request_time> cur_tick-delay_after_skill_request && last_skill_request_timeisKMoving()) { //_oprint( "stop: 3 [%5.0f-%5.0f] [%5.0f-%5.0f] %f-%f \n", m_sent_pos.x,player->getMyPos().x,m_sent_pos.y,player->getMyPos().y,m_sent_move_dir);// sent_pos, sent_dir,curr_pos,curr_dir stop_move( TRUE); } else { stop_move( FALSE ); } if (base_key_status == MOVE_NONE) { player->setKMoveType( MOVE_NONE ); } return; } // 방향 키가 눌린경우, 자동이동 취소. if (key_by_automove==0) { m_automove = FALSE; /* if (cur_state == SObjectState::STATE_CHASE) { K3DVector curr_pos = player->getMyPos(); SInputMove input_move( curr_pos ); m_gameSystem->AddGameInput( &input_move ); stop_move( FALSE ); return; } */ } //if (player->IsChasing()) // 키보드 이동은 추적을 캔슬 시킨다 //{ //player->m_pStateVM->StopCurrentState(); //player->clearAllState(); //player->m_pStateVM->ClearPendingNextState(); //} AR_TIME prev_time = m_arTime; AR_TIME curr_time = GetArTime(); AR_TIME elapsed_time = curr_time-prev_time; m_arTime = curr_time; int elapsed_sent = curr_time - m_sent_time; float curr_move_dir = get_key_move_dir(curr_key_status); float prev_move_dir = player->getKMoveDir(); int prev_key_status = player->getKMoveType() ; player->setKMoveType(curr_key_status); int direction_equal = sMathUtil::isEqual(prev_move_dir, curr_move_dir); int send_client = FALSE; int send_server = FALSE; if (prev_key_status != curr_key_status ) // 1.키이동시작 또는 키변경 { if (elapsed_sent>=st_interval && m_sent_key_status != curr_key_status) // 서버 패킷은 초당 5회 { send_server = TRUE; } else { send_client = TRUE; } } else if (prev_move_dir != curr_move_dir) // 2.키를 누른생태 또른 자동이동중 마우스로 방향 변경 { //float minAngle = sMathUtil::degreeToRadian(MIN_CHANGED_CAM_DEGREE); // 30도 이상 회전시 바로 패킷을 날린다동일방향으로 이동하면서 카메라로 방향을 바꾼경우. //if (fabs( curr_move_dir - m_sent_move_dir) >= minAngle) if (elapsed_sent>=st_interval && m_sent_move_dir != curr_move_dir) // 회전각보다 시간으로 제어 { send_server = TRUE; } else { send_client = TRUE; } } else // 3.누른 상태 또는 자동이동으로 동일방향 이동. { if (!m_moveUpdate.m_active) { if (player->IsMoving()==FALSE && player->IsChasing()==FALSE ) send_server = TRUE; // 다른 액션에 의해서 키보드 누르고 있는 상태가 유예. 이상황에서 elapsed 는? else return; } } // 일정시간이상 경과시 무조건 서버 전송 if (m_moveUpdate.update(elapsed_time)==FALSE) send_server = TRUE; // 이동 if (send_server || send_client) { AR_TIME elapsed; K3DVector target_pos; K3DVector curr_pos = get_local_pos(); int collision = get_move_pos( &curr_pos , &curr_move_dir, &target_pos, &elapsed, 1.0f ); if (collision==FALSE) { start_move(&curr_pos,curr_move_dir,curr_key_status, &target_pos, elapsed, send_server); // 충돌에의해 변경된 방향도 리턴되어야 하는가? if (send_server) _oprint( "start: 2 (%5.0f,%5.0f)-(%5.0f,%5.0f) %d %d (%d/%d)\n", curr_pos.x,curr_pos.y,target_pos.x,target_pos.y,elapsed, elapsed_time,send_server,send_client); } else { if (player->isKMoving()) { stop_move( TRUE ); _oprint( "stop: 2 (%5.0f,%5.0f)-(%5.0f,%5.0f) %d %d\n", curr_pos.x,curr_pos.y,target_pos.x,target_pos.y,elapsed, elapsed_time); } else { stop_move( FALSE ); } player->setKMoveType( MOVE_NONE ); } } } float SKMoveControler::get_key_move_dir(int key_status) { float fdir = m_gameCamera->getAngleZ() - sMathUtil::degree90; // = UP DIR // AziaMafia Clavier move if ((MOVE_UP & key_status) && (MOVE_LEFT & key_status) ) fdir += sMathUtil::degree45; else if ((MOVE_UP & key_status) && (MOVE_RIGHT & key_status) ) fdir -= sMathUtil::degree45; else if ((MOVE_DOWN & key_status) && (MOVE_RIGHT & key_status) ) fdir -= sMathUtil::degree135; else if ((MOVE_DOWN & key_status) && (MOVE_LEFT & key_status) ) fdir += sMathUtil::degree135; else if (MOVE_RIGHT& key_status) fdir -= sMathUtil::degree90; else if (MOVE_DOWN & key_status) fdir -= sMathUtil::degree180; else if (MOVE_LEFT & key_status) fdir += sMathUtil::degree90; else if (MOVE_UP & key_status) fdir += 0; return fdir; } bool SKMoveControler::get_move_pos( K3DVector* pcurr_pos, float* pdir, K3DVector* ptarget_pos, AR_TIME* pelapsed,float rate) { SGameAvatarEx* player = m_gameSystem->GetLocalPlayer(); if (!player) return TRUE; sNextKMoveInfo info; info.kmoveDir = *pdir; info.collisionTheta = *pdir; info.speed = player->getSpeed(); //if (info.speed<=0) info.speed = DEF_SPEED; if (info.speed<=0 || info.speed>200) info.speed = g_nMoveSpeed/7; if (player->GetCreatureMountHandle()) info.speed = player->getSpeed(); //if (info.speed<=0 || info.speed>100) info.speed = g_nMoveSpeed / 7; //info.speed = g_nMoveSpeed / 7; // AziaMafia FIx Speed info.setCurPos(pcurr_pos->x, pcurr_pos->y, pcurr_pos->z); info.checkNextCollisionPos = true; sArenaSystem* arenaSystem = g_pCurrentGameSystem->getArenaSystem(); bool collision = m_arObject->getNextKMovePos(info, st_move_length, arenaSystem->getBlockList(), rate); float nMovelength = sqrtf( (info.curPos.x - info.nextKMovePos.x) * (info.curPos.x - info.nextKMovePos.x) + (info.curPos.y - info.nextKMovePos.y) * (info.curPos.y - info.nextKMovePos.y) ); if ( nMovelength < (st_move_length/2.f) ) // 이동길이 / 2 보다 이동거리가 작을 땐 충돌로 처리 m_automove = FALSE; for (int i=0;i<2;i++) { if (collision==FALSE) break; if (i==0) info.collisionTheta = *pdir + 0.1f; else info.collisionTheta = *pdir - 0.1f; info.checkNextCollisionPos = true; info.collisionCount = 0; collision = m_arObject->getNextKMovePos(info, st_move_length, arenaSystem->getBlockList(), rate); } if (!collision) player->setCameraRoll(info.kmoveDir); /// 이동 방향과 충돌 후의 방향사이의 각이 90도가 넘으면 충돌로 간주해서 멈춘다. if (!collision && 0 < info.collisionCount) { float a1 = *pdir; sMathUtil::makeAbsTheta(a1); float a2 = info.kmoveDir; sMathUtil::makeAbsTheta(a2); float diff = a1 - a2; sMathUtil::makeAbsTheta(diff); //float t = min(diff, sMathUtil::degree360 - diff); if ( (diff>sMathUtil::degree90) && (diff <(sMathUtil::degree360-sMathUtil::degree90))) collision = true; *pdir = info.kmoveDir; } ptarget_pos->x = info.nextKMovePos.x; ptarget_pos->y = info.nextKMovePos.y; ptarget_pos->z = info.nextKMovePos.z; *pelapsed = info.elapsedArTime; return collision; } void SKMoveControler::send_move(K3DVector* ptarget_pos, float move_dir,int key_status, bool send_server) { SGameAvatarEx* player = m_gameSystem->GetLocalPlayer(); if (!player) return ; K3DVector target_pos = *ptarget_pos; // 서버에 전송 if (send_server) { AR_HANDLE ar_id = player->GetArID(); player->reqKMove(ar_id, target_pos); m_sent_key_status = key_status; m_sent_move_dir = move_dir; m_sent_pos = *ptarget_pos; m_sent_time = m_arTime; SObjectState::ID cur_state = player->m_pStateVM->GetCurrentState(); if (cur_state == SObjectState::STATE_CHASE) { //K3DVector curr_pos = player->getMyPos(); K3DVector target_pos = *ptarget_pos; SInputMove input_move( target_pos ); m_gameSystem->AddGameInput( &input_move ); stop_move( FALSE ); return; } } // 클라 이동 player->ClearWayPointList(); SMSG_KMOVE msg_move; msg_move.handle = player->GetArID(); msg_move.start_time = 0; msg_move.bReqMove = false; msg_move.pos_target = *ptarget_pos; msg_move.speed = player->getSpeed(); //if (msg_move.speed<=0) msg_move.speed = DEF_SPEED; if (msg_move.speed<=0 || msg_move.speed>200) msg_move.speed = g_nMoveSpeed/7; if (player->GetCreatureMountHandle()) msg_move.speed = player->getSpeed(); //msg_move.speed = g_nMoveSpeed /7; // AziaMafia FIx Speed ArPosition ar_pos = ArPosition(ptarget_pos->x, ptarget_pos->y, ptarget_pos->z); msg_move.m_vecMoveInfo.push_back(ar_pos); m_commandSystem->ProcMsgAtStatic(&msg_move); player->addKMoveTargetList(target_pos); } void SKMoveControler::send_last_move() { SGameAvatarEx* player = m_gameSystem->GetLocalPlayer(); if (!player) return ; K3DVector target_pos; AR_TIME elapsed; int key_status = player->getKMoveType(); //else kmoveDir = player->getKMoveDir(); // 이동을 멈출 때, 약간 이동을 더 시켜준다. K3DVector curr_pos = get_local_pos(); float curr_dir = get_key_move_dir(key_status); int collision = get_move_pos( &curr_pos, &curr_dir, &target_pos, &elapsed, st_stop_rate); //, )) if (collision == FALSE) { send_move(&target_pos, curr_dir, key_status,TRUE); _oprint( "stop: 2 (%5.0f,%5.0f)-(%5.0f,%5.0f)\n", curr_pos.x,curr_pos.y,target_pos.x,target_pos.y,elapsed); } else { send_move(&curr_pos, curr_dir, key_status,TRUE); _oprint( "block: 2 (%5.0f,%5.0f)-(%5.0f,%5.0f)\n", curr_pos.x,curr_pos.y,target_pos.x,target_pos.y,elapsed); } } void SKMoveControler::start_move(K3DVector* curr_pos, float curr_dir, int key_status, K3DVector* ptarget_pos, AR_TIME elapsed ,int send_server) { SGameAvatarEx* player = m_gameSystem->GetLocalPlayer(); if (!player) return ; m_gameWorld->ResetAutoMoveMode(); m_gameWorld->ResetAutoFollowMode(); if (player->IsMountMode()) player->ClearWayPointList(); player->EraseDetour(); send_move(ptarget_pos, curr_dir, key_status, send_server); AR_TIME updateTime = (AR_TIME)((float) elapsed * st_update_rate); // 0.8f m_moveUpdate.begin(updateTime); player->setKMoving(true); player->setKMoveType(key_status); } void SKMoveControler::stop_move(bool send_last) { SGameAvatarEx* player = m_gameSystem->GetLocalPlayer(); if (!player) return ; if (player->isKMoving() && send_last) send_last_move(); m_moveUpdate.end(); player->setKMoving( FALSE ); } /////////////////////////////////////////////////////////////////////////////////////////////////////// 이하 메시지 처리. // ?? void SKMoveControler::procMoveMsg(SGameMessage* msg) { SMSG_MOVE* moveMsg = dynamicCast(msg); if (!moveMsg)return ; SGameAvatarEx* player = m_commandSystem->GetAvatar(moveMsg->handle); if (player==NULL) return; if (player->IsLocalPlayer() && player->GetObjType() == TS_ENTER::GAME_PLAYER) { if (player->isKMoving()) { if (!player->isKMoveTargetList(moveMsg->m_vecMoveInfo)) { ArPosition p = moveMsg->m_vecMoveInfo.back(); stop_move( FALSE ); player->setKMoveType( MOVE_NONE ); /// 실제 자신의 위치로 이동 시킨다 ArMoveVector const& ar_pos = m_arObject->GetMyPos(); K3DVector pos(ar_pos.x, ar_pos.y, ar_pos.z); float curr_dir = m_sent_move_dir; int key_status = m_sent_key_status; send_move( &pos ,curr_dir, key_status, FALSE ); return ; } player->eraseKMoveTargetList( moveMsg->m_vecMoveInfo ); } } } // 마우스 미들버튼을 자동이동으로 사용하고 있다 void SKMoveControler::procMouseMsg(SGameMessage* msg) { SIMSG_MBUTTONDOWN* pmsg = dynamicCast(msg); if (pmsg==NULL) return ; if (pmsg->wParam & MK_MBUTTON) { m_automove = !m_automove; } } // 이동 서버 불허 판정 void SKMoveControler::procMsgResult(SGameMessage* msg) { SMSG_RESULT* pmsg = dynamicCast(msg); if (pmsg==NULL) return; if (pmsg->request_msg_id==TM_CS_MOVE_REQUEST) { m_servermove = TRUE; //if (pmsg->result==RESULT_ACCESS_DENIED) //{ // 최종컨펌받은 위치로 텔포? m_commandSystem->MoveImpossibleMsg(); //} // 서버에 의한 위치 중재 메시지로 활용? //SGameAvatarEx* player = m_commandSystem->GetLocalPlayer(); //if (player->isKMoving()) //{ stop_move( FALSE ); /// 실제 자신의 위치로 이동 시킨다 // player->setKMoveType( MOVE_NONE ); //} } }