Files
Sun1602/Server/GameServer/PlayerRider.cpp
T
2022-10-26 12:25:11 +08:00

1524 lines
55 KiB
C++

#include "stdafx.h"
#include "./PlayerRider.h"
#include <SCItemSlot.h>
#include <RidingInfoParser.h>
#include <SkillAttrCalculator.h>
//==================================================================================================
#include "PlayerWings.h"
#include "GameZone.h"
#include "GameZoneManager.h"
#include "ItemManager.h"
#include "InventorySlotContainer.h"
#include "./Status/StatusManager.h"
//==================================================================================================
//
// TODO : (091006) 몇몇 상투적으로 중복되는 부분에 대한 처리를 고민해 보자.
//==================================================================================================
//
#define REF_RIDER_DATA(var) \
static_cast<const PlayerRider::Container&>(static_cast<const IPlayerRiderD::ContainerBase&>(var))
static const BYTE IPlayerRiderD_Null[sizeof(IPlayerRiderD)];
PlayerRider::PlayerRider()
: delegator_((IPlayerRiderD&)IPlayerRiderD_Null) // dummy code
, is_transaction_(0)
, is_applied_option_(0)
{
ClearRiderItemInfo();
ClearGameControlInfo();
}
PlayerRider::PlayerRider(const IPlayerRiderD& r)
: delegator_(r) // dummy code
, is_transaction_(0)
, is_applied_option_(0)
{
ClearRiderItemInfo();
ClearGameControlInfo();
}
PlayerRider::~PlayerRider()
{
// release related resource
const Container& container = REF_RIDER_DATA(delegator_.container_);
if(container.is_summoned == 0)
return;
GameField* game_field = container.player->GetField();
if(game_field == 0)
return;
// (NOTE) current state description
// 소환된 정보가 있다면,
StatusManager* pStatusManager = container.player->GetStatusManager();
pStatusManager->ChangeInterState(eCHAR_STATE_RIDING_RIDER, false);
}
BOOLEAN
PlayerRider::GetRiderItemStream(RENDER_ITEMSTREAMEX* item_stream)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
if(FlowControl::FCAssert(container.is_summoned != 0 && rider_item_ != 0)) {
rider_item_->CopyOut(*item_stream);
return false;
}
return true;
}
// NOTE: f110411.2L, added the interface to support spread wings control on riding
bool PlayerRider::CanSpreadWings(const SCItemSlot& riding_item) const
{
#if !defined(_NA001955_110210_WING_COSTUME)
return false;
#endif
#ifdef _NA001955_110210_WING_COSTUME
const Container& container = REF_RIDER_DATA(delegator_.container_);
if (container.is_summoned == false || rider_item_ == NULL) {
return false;
};
if (rider_info_->flying_ == false) {
return false;
};
if (rider_item_ != &riding_item) {
return false;
};
return true;
#endif
}
bool PlayerRider::ChangeFlyingState(bool flied)
{
#if !defined(_NA001955_110210_WING_COSTUME)
return false;
#endif
#ifdef _NA001955_110210_WING_COSTUME
const Container& container = REF_RIDER_DATA(delegator_.container_);
if (container.is_summoned == false || rider_item_ == NULL) {
return false;
};
if (rider_info_->flying_ == false) {
return false;
};
assert(flied_ != flied);
flied_ = flied;
return true;
#endif
}
//==================================================================================================
PlayerRider::eResult
PlayerRider::Route(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data)
{
BOOST_STATIC_ASSERT(eResult_Fail == 0);
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
eResult success = eResult_Fail;
switch(req_info.request)
{
case req_info.eReq_SummonStart:
case req_info.eReq_SummonCancel:
success = SummonCasting(req_info, result, result_data);
break;
case req_info.eReq_Summon:
success = Summon(req_info, result, result_data);
break;
case req_info.eReq_SummonBattleRelease: //전투를 위한 소환해제
case req_info.eReq_SummonRelease: //일반 소환 해제
success = SummonRelease(req_info, result, result_data);
break;
case req_info.eReq_WaxGloss:
success = WaxGloss(req_info, result, result_data, container.player);
break;
case req_info.eReq_EquipParts:
success = Equip(req_info, result, result_data);
break;
case req_info.eReq_UnEquipParts:
success = UnEquip(req_info, result, result_data);
break;
default:
result->Clear();
result_data->Clear();
IPlayerRiderD::SetDefaultFailResult(req_info, result, result_data);
success = eResult_Fail;
break;
}
return success;
}
BOOLEAN
PlayerRider::CheckRule(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data, eResult* broad)
{
__UNUSED(result_data);
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
*broad = eResult_Fail;
const eCHAR_TYPE char_type = container.player->GetCharType();
__UNUSED(char_type); // debug
StatusManager* pStatusManager = container.player->GetStatusManager();
BOOLEAN is_req_casting_start = req_info.request == req_info.eReq_SummonStart;
BOOLEAN is_req_summon = is_req_casting_start == 0 &&
req_info.request == req_info.eReq_Summon;
if (is_req_casting_start || is_req_summon)
{
// (CHANGES) (f100514.1L) add filter rule
const StatusField& status_field = pStatusManager->Status;
if (status_field.IsDragonTransforming() ||
status_field.IsPlayer_TransOn() ||
pStatusManager->CanSpecialAction() == false) // CHANGES: f110315.2L
{
// remove a status find code
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_FAILED;
*broad = is_req_casting_start ? eResult_Fail : eResult_1toN;
return false;
}
}
return true;
}
// casting start|cancel
PlayerRider::eResult
PlayerRider::SummonCasting(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
result->Clear();
result_data->Clear();
// client passing data
result->request = req_info.request;
result->args = req_info.args;
if(container.is_summoned != 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_ALREADY_SUMMONED;
return eResult_Fail;
}
if(container.player->IsDead()) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_Fail;
}
if(container.player->IsMoving()) {
container.player->StopMoving();
}
eResult rule_result;
if(CheckRule(req_info, result, result_data, &rule_result) == 0)
return rule_result;
BOOLEAN success = true;
if(req_info.request == req_info.eReq_SummonStart) {
if(container.state.state == req_info.eReq_SummonStart) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_SUMMON_CASTING;
return eResult_Fail;
}
Container& container_edit = const_cast<Container&>(container);
container_edit.state.request = req_info.eReq_SummonStart;
container_edit.state.args = req_info.args;
container_edit.casting_start = GetTickCount();
}
else if(req_info.request == req_info.eReq_SummonCancel) {
if(container.state.state != req_info.eReq_SummonStart) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_FAILED;
return eResult_Fail;
}
Container& container_edit = const_cast<Container&>(container);
container_edit.state.state = req_info.eReq_None;
container_edit.casting_start = 0;
}
else {
success = false;
}
if(success) {
result->result = req_info.eRet_Success;
result_data->data_type = result_data->eDataType_Actor;
RiderControlData::sActor& actor = result_data->actor;
actor.requester = (WORD)container.player->GetObjectKey();
ClearRiderItemInfo();
return eResult_1toN;
}
return eResult_Fail;
}
PlayerRider::eResult
PlayerRider::Summon(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
result->Clear();
result_data->Clear();
// client passing data
result->request = req_info.request;
result->args = req_info.args;
if(container.state.state != req_info.eReq_SummonStart) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_FAILED;
return eResult_Fail;
}
if(container.state.args.casting_start.slot_pos != req_info.args.summon.slot_pos) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_FAILED;
return eResult_1toN;
}
// need state released
const DWORD current_tick = GetTickCount();
DWORD casting_start = container.casting_start;
{
Container& container_edit = const_cast<Container&>(container);
container_edit.state.state = req_info.eReq_None;
container_edit.casting_start = 0;
}
WORD player_key = (WORD)container.player->GetObjectKey();
if(container.is_summoned != 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_ALREADY_SUMMONED;
return eResult_Fail;
}
if(item_pos_ != INVALID_SLOT_POS || rider_item_ != 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
ePLAYER_BEHAVE_STATE behave = container.player->GetBehave();
if(behave != PLAYER_BEHAVE_IDLE_STATE) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_UNUSABLE_FUNCTION;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
if(container.player->IsDead()) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_1toN;
}
eResult rule_result;
if(CheckRule(req_info, result, result_data, &rule_result) == 0)
return rule_result;
POSTYPE rider_item_pos = req_info.args.summon.slot_pos;
// (NOTE) (100202) 변경된 제어룰. 인벤잠금 상태 고려할 것
RiderItemQueryResult query_result = { 0, };
RC::eITEM_RESULT item_result = GetRiderItem(rider_item_pos, &query_result);
if(item_result != RC::RC_ITEM_SUCCESS) {
result->result = req_info.eRet_ItemFailed;
result->result_code = item_result;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
SCItemSlot* rider_item = query_result.rider_item; // compatible interface
// (NOTE) 게임서버의 제어가 벗어나 있는 상태에서 들어올 경우, 여기서 문제가 발생할 수 있다.
// 어캐...해야 하나... 최소한 2개가 summoned로 설정되어 있다면... 음...
if(rider_item->IsRiderSummoned() != 0) {
if(is_transaction_) { // field move transaction by 'OnEnterField'
// in GameServerControl
rider_item->RiderSummonStatus(false); // no update
is_transaction_ = false;
}
else if (query_result.inventory_lock) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVENTORY_NEED_PASSWORD;
return eResult_Fail;
}
else {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED;
return eResult_Fail;
}
}
// check time expiration
if(rider_item->IsExpiredWaitTime() != 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_TIME_EXPIRED;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
if (rider_item->CanSummonStatus() == false) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_NOT_RIDER_ITEM;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
const BASE_ITEMINFO* rider_item_info = rider_item->GetItemInfo();
DWORD rider_info_index = rider_item_info->m_wSkillCode;
RidingInfoParser* const pRidingInfoParser = RidingInfoParser::Instance();
const sRiderInfo* rider_info = pRidingInfoParser->GetInfo(rider_info_index);
if(rider_info == 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_CANT_FIND_INFO;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
GameZone* game_zone = container.player->GetGameZonePtr();
{ // (NOTE) game_zone이 null일 상황은 있을 수 없다. already check 'GetRiderItem'
// (CHANGES) (WOPS:5607) 타락한 사원 내 소환 요청을 막는다.
eZONETYPE zone_type = game_zone ? game_zone->GetZoneType() : eZONETYPE_MAX;
if(rider_info->IsAcceptField(zone_type) == 0 ||
!!(game_zone->Policyment.FIELD & game_zone->Policyment.ENABLE_SSQ_CONTROL))
{
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_UNABLE_FUNCTION_FOR_CUR_ZONE;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
};
// casting 100초 후라 할 경우 약 88초부터 허용해 준다.
casting_start += rider_info->spawn_timer_ - (rider_info->spawn_timer_ >> 3);
if(current_tick < casting_start) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_SUMMON_CASTING;
// 이 경우는 원래값 복원하자. 아,, 트랜젝션 이상해 질지도...
//container_edit.casting_start = casting_start;
//container_edit.state.state = req_info.eReq_SummonStart;
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
return eResult_1toN;
}
//-------------------------------------//
//-------------- success --------------//
// (WARNING) if success == 0 then
// need release resource
BOOLEAN success = true;
{// register item related info
item_pos_ = rider_item_pos;
rider_item_ = rider_item;
rider_info_ = rider_info;
};
// (CHANGES) (WAVERIX) (091022) clear local field for game control
this->ClearGameControlInfo();
nsSlot::RiderUpdateBits update_bits = rider_item_->RiderSummonStatus(true);
// (WARNING) 해당 기능은 transaction을 만족시켜야 한다. 적용 도중에 실패하면 대략 낭패
if(success && ApplyOption(*rider_item_, update_bits) == 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED; // (NOTE) 에러를 재정의할 필요 있음.
result_data->data_type = result_data->eDataType_Actor;
result_data->actor.requester = player_key;
success = false;
}
//---- success control ----//
if(success) {
// change flag
Container& container_edit = const_cast<Container&>(container);
container_edit.is_summoned = true;
StatusManager* pStatusManager = container.player->GetStatusManager();
pStatusManager->ChangeInterState(eCHAR_STATE_RIDING_RIDER, true);
// notification
result->result = req_info.eRet_Success;
result_data->data_type = result_data->eDataType_ItemAction;
RiderControlData::sItemAction& item_action = result_data->item_action;
item_action.requester = player_key;
item_action.update_bits = update_bits;
rider_item_->CopyOut(item_action.item_slot);
// change state
//player_.SetBehave(PLAYER_BEHAVE_RIDING_STATE);
return eResult_1toN;
}
//---- fail control ----//
if(success == 0) {
ClearRiderItemInfo();
}
return eResult_1toN;
}
PlayerRider::eResult
PlayerRider::SummonRelease(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
result->Clear();
result_data->Clear();
// client passing data
result->request = req_info.request;
result->args = req_info.args;
if(container.is_summoned == 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_ALREADY_SUMMON_RELEASED;
return eResult_Fail;
}
if(item_pos_ == INVALID_SLOT_POS || rider_item_ == 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED;
return eResult_Fail;
}
// ##problem##
if(rider_item_->IsRiderSummoned() == 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_ALREADY_SUMMON_RELEASED;
return eResult_Fail;
}
ePLAYER_BEHAVE_STATE behave = container.player->GetBehave();
if(behave != PLAYER_BEHAVE_IDLE_STATE) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_UNUSABLE_FUNCTION;
return eResult_Fail;
}
POSTYPE rider_item_pos = req_info.args.summon.slot_pos;
if(item_pos_ != rider_item_pos) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
GameField* pGameField = container.player->GetField();
if(pGameField == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_Fail;
}
// no-check 'dead' state
// (NOTE) 아이템 삭제 등등의 예기치 못한 이유가 발생시 // ##problem##위치에서부터
// 문제를 야기할 수 있다. 따진다면 다른 곳에서도 유사 문제 발생할 수 있는데...
// DeleteSlot 등에서의 문제를 고민해 보자. 일단은 정황상 캐시템을 삭제하면서까지 능력치를 올리는
// 짱구들이 존재할 지도 모른다. 테섭이라면 특히 가능성 높다.
// (NOTE) (100202) 변경된 제어룰. 인벤잠금 상태 고려할 것
RiderItemQueryResult query_result = { 0, };
RC::eITEM_RESULT item_result = GetRiderItem(rider_item_pos, &query_result);
if(item_result != RC::RC_ITEM_SUCCESS) {
result->result = req_info.eRet_ItemFailed;
result->result_code = item_result;
return eResult_Fail;
}
SCItemSlot* rider_item = query_result.rider_item; // compatible interface
if(rider_item_ != rider_item) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED;
return eResult_Fail;
}
//-------------------------------------//
//-------------- success --------------//
#ifdef _NA001955_110210_WING_COSTUME
if (this->flied_)
{
bool result = container.player->Wings.Command_Furl(IVehicleEvent::LinkEvent());
assert(result);
this->flied_ = false;
}
#endif
// (WARNING)
//
BOOLEAN success = true;
nsSlot::RiderUpdateBits update_bits = rider_item_->RiderSummonStatus(false);
// (WARNING) 해당 기능은 transaction을 만족시켜야 한다. 적용 도중에 실패하면 대략 낭패
if(success && CancelOption(*rider_item_, update_bits) == 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED; // (NOTE) 에러를 재정의할 필요 있음.
success = false;
//...transaction이 보장된다는 전제에서는 그냥 에러 반환해도 될 거라 생각된다.
return eResult_Fail;
}
if(success) {
// change flag
Container& container_edit = const_cast<Container&>(container);
container_edit.is_summoned = false;
StatusManager* pStatusManager = container.player->GetStatusManager();
pStatusManager->ChangeInterState(eCHAR_STATE_RIDING_RIDER, false);
// notification
result->result = req_info.eRet_Success;
result_data->data_type = result_data->eDataType_Actor;
RiderControlData::sActor& actor = result_data->actor;
actor.requester = (WORD)container.player->GetObjectKey();
ClearRiderItemInfo();
return eResult_1toN;
}
if(success == 0) {
// nothing to do 'current'
}
return eResult_Fail;
}
PlayerRider::eResult
PlayerRider::Equip(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
result->Clear();
result_data->Clear();
// client passing data
result->request = req_info.request;
result->args = req_info.args;
const RiderControl::sReq_Equip& equip_info = req_info.args.equip;
if(container.is_summoned == 0) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_ALREADY_SUMMON_RELEASED;
return eResult_Fail;
}
ePLAYER_BEHAVE_STATE behave = container.player->GetBehave();
if(behave != PLAYER_BEHAVE_IDLE_STATE) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_UNUSABLE_FUNCTION;
return eResult_Fail;
}
ItemManager* item_manager = container.player->GetItemManager();
if(item_manager->ValidState() == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_Fail;
}
if(item_manager->ValidPos(SI_INVENTORY, equip_info.from, true) == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
CInventorySlotContainer* inventory =
static_cast<CInventorySlotContainer*>(item_manager->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& parts_item = static_cast<SCItemSlot&>(inventory->GetSlot(equip_info.from));
if(parts_item.IsContainValidItem() == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_Fail;
}
if(parts_item.GetItemInfo()->IsRiderPartsItem() == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
if(rider_item_->IsExpiredWaitTime() || parts_item.IsExpiredWaitTime()) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_TIME_EXPIRED;
return eResult_Fail;
}
RC::eITEM_RESULT item_result = rider_item_->CanEquipRiderParts(parts_item);
if(item_result != RC::RC_ITEM_SUCCESS) {
result->result = req_info.eRet_ItemFailed;
result->result_code = item_result;
return eResult_Fail;
}
if(parts_item.OnEquip()) { // 처음 장착 여부이다.
GAMELOG->LogItem(ITEM_CHARGE_FIRST_EQUIP, container.player, &parts_item);
}
SCItemSlot parts_item_out;
if(inventory->DeleteSlot(equip_info.from, &parts_item_out) == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_Fail;
}
if(!FlowControl::FCAssert(rider_item_->EquipRiderParts(parts_item_out))) {
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED;
return eResult_Fail;
}
nsSlot::RiderUpdateBits update_bits = rider_item_->GetRiderAttrChangedInfo();
// update event
ApplyOption(*rider_item_, update_bits);
result->request = req_info.request;
result->result = req_info.eRet_Success;
result_data->data_type = result_data->eDataType_ItemAction;
RiderControlData::sItemAction& item_action = result_data->item_action;
item_action.requester = (WORD)container.player->GetObjectKey();
item_action.update_bits = update_bits;
rider_item_->CopyOut(item_action.item_slot);
return eResult_1toN;
}
PlayerRider::eResult
PlayerRider::UnEquip(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
result->Clear();
result_data->Clear();
// client passing data
result->request = req_info.request;
result->args = req_info.args;
const RiderControl::sReq_UnEquip& unequip_info = req_info.args.unequip;
// (NOTE) 장착 해제의 경우는 소환 여부와 상관 없이, 해당 라이더 아이템에서 해제할 수 있다.
// case : 정액 만료된 라이더의 인벤토리를 열어 파츠를 추출한다.
// can't use 'rider_item_'
POSTYPE summoned_item_pos = INVALID_POSTYPE_VALUE;
POSTYPE from_rider_item_pos = unequip_info.where;
if(container.is_summoned != 0) {
summoned_item_pos = rider_item_->GetPos();
//POSTYPE summoned_item_pos = rider_item_->GetPos();
//if(from_rider_item_pos != summoned_item_pos) {
// result->result = req_info.eRet_Failed;
// result->result_code = RC::RC_RIDER_MISMATCH_SUMMON_RIDER;
// return eResult_Fail;
//}
}
if(container.player->GetField() == 0 || container.player->IsDead()) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_Fail;
}
ePLAYER_BEHAVE_STATE behave = container.player->GetBehave();
if(behave != PLAYER_BEHAVE_IDLE_STATE) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_UNUSABLE_FUNCTION;
return eResult_Fail;
}
// (NOTE) (100202) 변경된 제어룰. 인벤잠금 상태 고려할 것
// can't use 'rider_item_'
RiderItemQueryResult query_result = { 0, };
RC::eITEM_RESULT item_result = GetRiderItem(from_rider_item_pos, &query_result);
if(item_result != RC::RC_ITEM_SUCCESS) {
result->result = req_info.eRet_ItemFailed;
result->result_code = item_result;
return eResult_Fail;
}
SCItemSlot* rider_item = query_result.rider_item; // compatible interface
ItemManager* item_manager = container.player->GetItemManager();
if(item_manager->ValidPos(SI_INVENTORY, unequip_info.to, false) == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
nsSlot::RiderUpdateBits update_bits; // for sub option
if(rider_item->CanUnEquipRiderParts(unequip_info.from, &update_bits) == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
assert(update_bits.value == rider_item->GetRiderAttrChangedInfo().value);
//----------------------------------------------------------------------------------------------
// desire : must be sussess
SCItemSlot rider_item_pre_unequiped = *rider_item;
SCItemSlot parts_item;
if(rider_item->UnEquipRiderParts(unequip_info.from, &parts_item) == 0) {
ASSERT(!"Terrible");
result->result = req_info.eRet_Failed;
result->result_code = RC::RC_RIDER_UNEXPECTED_FAILED;
return eResult_Fail;
}
//위에서는 통과하고 하기 상황이 위반되는 경우는 기한 만료된 아이템의 경우다.
//기한 만료는 ItemManager::Update에 의한 'Command_ItemSync'에 의해 변경되고 이미 스탯도 변경된다.
// - 정리하면 다음과 같다. UnEquip:Success가 발생한 Rider아이템의 경우는 이미 parts가 추출된 상태
//이기 때문에 업데이트에 문제가 생긴다. 또한 기한 만료가 발생하면서도 parts는 비활성 상태로 rider
//인벤토리에 존재해야 하기 때문에 서로 다른 업데이트 형식이 발생하게 된다.
// 따라서 UnEquip Process에는 두 단계의 서로 다른 UpdateBits가 발생할 수 있는 상황으로 구분해서
//처리한다.
//
//assert(update_bits.value == rider_item->GetRiderAttrChangedInfo().value);
parts_item.OnUnEquip();
CInventorySlotContainer* inventory =
static_cast<CInventorySlotContainer*>(item_manager->GetItemSlotContainer(SI_INVENTORY));
// 에러 처리 고민해 볼 것
RC::eSLOT_INSERT_RESULT slot_result = inventory->InsertSlot(unequip_info.to, parts_item);
if(slot_result != RC::RC_INSERT_SLOT_SUCCESS) {
ASSERT(!"Terrible");
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;//unexpected
return eResult_Fail;
}
if(summoned_item_pos == from_rider_item_pos)
CancelOption(rider_item_pre_unequiped, update_bits);
// desire : must be sussess
//----------------------------------------------------------------------------------------------
/*
SCRiderPartsData parts_item_info;
if(rider_item->GetRiderPartsInfo(unequip_info.from, &parts_item_info) == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
if(parts_item_info.item_code_ == 0) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
nsSlot::RiderUpdateBits update_bits_for_server = { 0, };
if(summoned_item_pos == from_rider_item_pos) {
// not cleared logic...
BYTE parts_index = parts_item_info.parts_type_.value - parts_item_info.parts_type_.StartIndex;
update_bits_for_server.value |= (1 << parts_index) << 5;
CancelOption(update_bits_for_server);
}
nsSlot::RiderUpdateBits update_bits = rider_item->GetRiderAttrChangedInfo();
CInventorySlotContainer* inventory =
static_cast<CInventorySlotContainer*>(item_manager->GetItemSlotContainer(SI_INVENTORY));
// 에러 처리 고민해 볼 것
RC::eSLOT_INSERT_RESULT slot_result = inventory->InsertSlot(unequip_info.to, parts_item);
if(slot_result != RC::RC_INSERT_SLOT_SUCCESS) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
*/
result->request = req_info.request;
result->result = req_info.eRet_Success;
result_data->data_type = result_data->eDataType_ItemAction;
RiderControlData::sItemAction& item_action = result_data->item_action;
item_action.requester = (WORD)container.player->GetObjectKey();
rider_item->CopyOut(item_action.item_slot);
if(summoned_item_pos != from_rider_item_pos) {
item_action.update_bits.value = 0;
return eResult_1to1;
}
item_action.update_bits = update_bits;
return eResult_1toN;
}
//==================================================================================================
PlayerRider::eResult
PlayerRider::WaxGloss(const RiderControl& req_info,
RiderControl* result, RiderControlData* result_data,
Player* player)
{
//const Container& container = REF_RIDER_DATA(delegator_.container_);
result->Clear();
result_data->Clear();
// client passing data
result->request = req_info.request;
result->args = req_info.args;
BOOLEAN success = true;
if(player->GetField() == 0 || player->IsDead()) {
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDSTATE;
return eResult_Fail;
}
const RiderControl::sReq_WaxGloss& wax_info = req_info.args.wax_gloss;
ItemManager* item_manager = player->GetItemManager();
if(success && (item_manager->ValidPos(SI_INVENTORY, wax_info.from_wax, TRUE) == 0 ||
item_manager->ValidPos(SI_INVENTORY, wax_info.to_parts, TRUE) == 0))
{
success = false;
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALIDPOS;
return eResult_Fail;
}
CInventorySlotContainer* inventory =
static_cast<CInventorySlotContainer*>(item_manager->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& from_wax_item = static_cast<SCItemSlot&>(inventory->GetSlot(wax_info.from_wax));
SCItemSlot& to_parts_item = static_cast<SCItemSlot&>(inventory->GetSlot(wax_info.to_parts));
const BASE_ITEMINFO* from_wax_info = from_wax_item.GetItemInfo();
const BASE_ITEMINFO* to_parts_info = to_parts_item.GetItemInfo();
if(success && (from_wax_info->m_wType != eITEMTYPE_WAX ||
to_parts_info->IsRiderPartsItem() == 0))
{
success = false;
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALID_TYPE;
return eResult_Fail;
}
const WORD wax_enchant = from_wax_info->m_wSkillCode;
// 정의된 값 없나?
//if(success && (wax_enchant < 12 + 1) == 0)
// Use이후 광택 성공시키기 위해 광택 낼 수 있는 지 선행 검사 필요함
{
WORD item_code_out = 0;
RC::eITEM_RESULT ret = item_manager->Use(SI_INVENTORY, wax_info.from_wax, item_code_out);
if(ret != RC::RC_ITEM_SUCCESS) {
success = false;
result->result = req_info.eRet_ItemFailed;
result->result_code = RC::RC_ITEM_INVALID_TYPE;
return eResult_Fail;
}
};
// ItemManager::Use에 의해 소진|삭제|이벤트 처리가 되었을 터... 나머지는 반드시 성공시켜야 하는
// 파츠 광택 내기...
to_parts_item.SetEnchant((BYTE)wax_enchant);
assert(success != 0);
result->request = req_info.request;
result->result = req_info.eRet_Success;
result_data->data_type = result_data->eDataType_ItemAction;
RiderControlData::sItemAction& item_action = result_data->item_action;
item_action.requester = (WORD)player->GetObjectKey();
to_parts_item.CopyOut(item_action.item_slot);
return eResult_1to1;
}
//==================================================================================================
// (DESC) runtime check routine
BOOLEAN
PlayerRider::IsValidCurrentContainedItemSlot() const
{
// (NOTE) 아직 어디까지 검증을 해야 확실하지 않는 관계로 가능한 정보들을 모두 추출해 둔다.
// 확정로직일 경우 적절히 배제 시키기로 한다.
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(container.player != 0);
if(container.is_summoned == 0)
return false;
if(item_pos_ == INVALID_SLOT_POS || //assert(SI_INVENTORY)
rider_item_ == 0 ||
rider_info_ == 0)
{
return false;
}
if(rider_item_->IsContainValidItem() == 0)
return false;
POSTYPE item_pos = rider_item_->GetPos();
if(item_pos != item_pos_)
return false;
// 이건... 시리얼 키를 받는 시점과 미묘하게 핀트가 어긋난다. 좀 더 좋은 방법을 강구할 것
DBSERIAL serial_key = rider_item_->GetSerial();
if(serial_key != rider_item_serial_)
return false;
ItemManager* item_manager = container.player->GetItemManager();
if(item_manager->ValidPos(SI_INVENTORY, item_pos, TRUE) == 0)
return false;
CInventorySlotContainer* inventory =
static_cast<CInventorySlotContainer*>(item_manager->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& rider_item = static_cast<SCItemSlot&>(inventory->GetSlot(item_pos));
if(&rider_item != rider_item_)
return false;
// no checked 'rider_item_->IsRiderSummoned()'
if(rider_item_->IsRiderItem() == 0)
return false;
return true;
}
RC::eITEM_RESULT
PlayerRider::GetRiderItem(POSTYPE request_item_pos, RiderItemQueryResult* query_result)
{
//assert player_ != 0
const Container& container = REF_RIDER_DATA(delegator_.container_);
assert(query_result->rider_item == NULL);
ItemManager* item_manager = container.player->GetItemManager();
// (NOTE) item_manager->ValidState()는 IsDead도 검사한다. 이건 소환 해제시에는 문제 발생
//if(item_manager->ValidState() == 0)
// return RC::RC_ITEM_INVALIDSTATE;
if (container.player->GetField() == 0) {
return RC::RC_ITEM_INVALIDSTATE;
}
{ // validation test queried item by pos
// to skip inventory lock
// (NOTE) prevent inventory lock status open/close action in packet handler
ItemManager::TransactionClosure transaction(item_manager,
ItemManager::TransactionClosure::eTransType_RiderQuery);
if (item_manager->ValidPos(SI_INVENTORY, request_item_pos, TRUE) == 0) {
return RC::RC_ITEM_INVALIDPOS;
}
ProtectorInventory& protector = \
container.player->GetWarehouseInventoryProtector().INVENTORY();
if (protector.IsUsable() == false) {
query_result->inventory_lock = true;
}
};
CInventorySlotContainer* inventory = \
static_cast<CInventorySlotContainer*>(item_manager->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& rider_item = static_cast<SCItemSlot&>(inventory->GetSlot(request_item_pos));
if (rider_item.IsContainValidItem() == false) {
return RC::RC_ITEM_INVALIDSTATE;
}
if (rider_item.IsRiderItem() == false) {
return RC::RC_ITEM_INVALIDPOS;
}
const BASE_ITEMINFO* item_info = rider_item.GetItemInfo();
RC::eITEM_RESULT result = container.player->CanUseItem(item_info);
if (result != RC::RC_ITEM_SUCCESS) {
return result;
}
query_result->rider_item = &rider_item;
return RC::RC_ITEM_SUCCESS;
}
BOOLEAN
PlayerRider::OnChangedOption(const nsSlot::RiderUpdateBits& update_bits)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
// 소환
PlayerAttributes& attributes = static_cast<PlayerAttributes&>(container.player->GetAttr());
SkillAttrCalculator skill_calculator(attributes);
bool is_pc_room = (container.player->CanApplyPCBangBenefit(true) != false);
skill_calculator.ApplyRidingRider(false, is_pc_room, *rider_item_, update_bits);
skill_calculator.ApplyRidingRider(true, is_pc_room, *rider_item_, update_bits);
return true; // test code
}
BOOLEAN
PlayerRider::ApplyOption(const SCItemSlot& rider_item, const nsSlot::RiderUpdateBits& update_bits)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
PlayerAttributes& attributes = static_cast<PlayerAttributes&>(container.player->GetAttr());
SkillAttrCalculator skill_calculator(attributes);
bool is_pc_room = (container.player->CanApplyPCBangBenefit(true) != false);
skill_calculator.ApplyRidingRider(true, is_pc_room, rider_item, update_bits);
return true; // test code
}
BOOLEAN
PlayerRider::CancelOption(const SCItemSlot& rider_item, const nsSlot::RiderUpdateBits& update_bits)
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
PlayerAttributes& attributes = static_cast<PlayerAttributes&>(container.player->GetAttr());
SkillAttrCalculator skill_calculator(attributes);
bool is_pc_room = (container.player->CanApplyPCBangBenefit(true) != false);
skill_calculator.ApplyRidingRider(false, is_pc_room, rider_item, update_bits);
return true; // test code
}
//float
//PlayerRider::GetMoveSpeedRatio() const
//{
// // TODO(WAVERIX) AttrValues를 이용한 적용이 필요하다.
// return 1.0f;
//}
//==================================================================================================
//==================================================================================================
//==================================================================================================
IPlayerRiderD::~IPlayerRiderD()
{
BOOST_STATIC_ASSERT(sizeof(IPlayerRiderD::ContainerExport) >= sizeof(PlayerRider::Container));
REF_RIDER_DATA(container_); //compile-time checker
if(container_.handler != 0)
{
}
}
void IPlayerRiderD::Init(Player* player)
{
if(!FlowControl::FCAssert(player && container_.handler == 0))
return;
new (this) IPlayerRiderD;
container_.handler = TAllocNew(PlayerRider);
new (container_.handler) PlayerRider(*this);
container_.player = player;
}
void IPlayerRiderD::Release()
{
if(container_.handler != 0) {
TAllocDelete(PlayerRider, container_.handler);
container_.handler = 0;
}
new (this) IPlayerRiderD;
}
//==================================================================================================
// (NOTE) IPlayerRiderD::Update는 PlayerRider가 instance되지 않고 액션이 일어날 경우를 가정한 것이다.
// 많은 연산이 일어날 수 있으므로, PlayerRider.cpp에 구현해 둔다.
void IPlayerRiderD::Update(Player* player, DWORD delta_tick)
{
__UNUSED(delta_tick);
const DWORD MaxInterval = 20 * 1000;
DWORD current_tick = GetTickCount();
const PlayerRider::Container& container = REF_RIDER_DATA(container_);
switch(container.state.state)
{
case container.state.eReq_SummonStart:
assert(container.handler);
break;
default:
return;
};
RiderControl cmd = { 0, };
MSG_CG_ITEM_RIDER_CONTROL_BRD msgBRD;
BOOLEAN to_client_notify = false;
PlayerRider::eResult result = PlayerRider::eResult_Fail;
switch(container.state.state)
{
case container.state.eReq_SummonStart:
// casting 시작 후 MaxInterval(20sec)동안 소환 요청이 오지 않는다면 cancel시킨다.
// for casting ani rendering synchronization
if(container.casting_start + MaxInterval < current_tick) {
cmd.request = cmd.eReq_SummonCancel;
result = container.handler->SummonCasting(cmd, &msgBRD.header, &msgBRD.data);
to_client_notify = true;
}
break;
}
if(to_client_notify) {
if(result == PlayerRider::eResult_1toN)
player->SendPacketAround(&msgBRD, msgBRD.GetSize(), true);
else
player->SendPacket(&msgBRD, msgBRD.GetSize());
}
}
//--------------------------------------------------------------------------------------------------
// special commands : for field<->battle synchronization
// serializing from DBStream : first status
BOOLEAN
IPlayerRiderD::SyncPrepareRidingStatus(KEYTYPE enter_zone_key)
{
const PlayerRider::Container& container = REF_RIDER_DATA(container_);
if(!FlowControl::FCAssert(container.state.request == container.state.eReq_None &&
container.enter_state.request == container.enter_state.eReq_None &&
container.transaction_mode == container.eTrans_None))
{
return false;
}
PlayerRider::Container& container_edit = const_cast<PlayerRider::Container&>(container);
container_edit.transaction_mode = container_edit.eTrans_SerializeStart;
container_edit.transaction_zone_key = enter_zone_key;
return true;
}
// serializing notification : OnUpdate
// (NOTE) important!!
// - 클라이언트로 보내지기 전의 서버쪽 인벤토리를 설정하는 시점에 호출되는 함수이며,
// 이곳에서 입장하는 존의 성격|라이더 정보등을 고려해 소환상태 유지|해제 등이 이뤄질 수 있도록 지원
// 한다. 아이템 스트림을 받는 시점(최초 입장|필드<->배틀|기타...)에 해당 스트림이 전송될 경우, 클라
// 이언트의 경우는 그 정보를 보고 소환 상태를 결정하고 능력치 계산이 이뤄진다.
BOOLEAN
IPlayerRiderD::SyncNotifyRiderSummon(const SCItemSlot& item_slot, Player* player)
{
const SLOTIDX slot_idx = item_slot.GetSlotIdx();
const POSTYPE rider_item_pos = item_slot.GetPos();
if(slot_idx != SI_INVENTORY || rider_item_pos == INVALID_POSTYPE_VALUE)
return false;
const PlayerRider::Container& container = REF_RIDER_DATA(container_);
if(container.enter_state.request == container.enter_state.eReq_Summon) {
if(container.enter_state.args.summon.slot_pos == rider_item_pos)
return true;
return false;
}
BOOLEAN valid_check = item_slot.IsRiderItem() && item_slot.IsRiderSummoned();
if(!FlowControl::FCAssert(valid_check != 0))
return false;
if(item_slot.IsExpiredWaitTime() != 0)
return false;
// (NOTE) 이후 로직은 입장 시점의 라이더 소환 유지 상태를 위한 것이므로 하기 조건을 이용해 필터
// 링한다. transaction_mode != eTrans_SerializeStart인 상태는 장비 업데이트(+인벤)일 경우 발동할
// 수 있다.
if(container.transaction_mode != container.eTrans_SerializeStart)
return true;
// Check enter zone rule by RidingInfo
const BASE_ITEMINFO* item_info = item_slot.GetItemInfo();
const sRiderInfo* rider_info = RidingInfoParser::Instance()->GetInfo(item_info->m_wSkillCode);
if(!FlowControl::FCAssert(rider_info != 0))
return false;
GameZone* game_zone = g_GameZoneManager.GetZone(container.transaction_zone_key);
if(game_zone == 0) // case : field released during db transaction : asserting logic
return false;
// if (summoned AND can't summon field) then change to summon released
eZONETYPE zone_type = game_zone->GetZoneType();
if(rider_info->IsAcceptField(zone_type) == 0) {
// case study : exist two methods
// #1: 1st enter field
// #2: enter field events over than 1st
ePLAYER_STATE player_state = (ePLAYER_STATE)player->GetState();
if(player_state == PLAYER_CHARINFO_REQUESTED) {
//irregular... 일단, serializing 상태이자 sync 명령으로 전환되었다.
const_cast<SCItemSlot&>(item_slot).RiderSummonStatus(false);
Command_ItemSync(player, item_slot);
}
return false;
}
assert(container.handler == 0 && this == &player->Rider);
player->Rider.Init(player);
PlayerRider::Container& container_edit = const_cast<PlayerRider::Container&>(container);
container_edit.enter_state.request = container.enter_state.eReq_Summon;
container_edit.enter_state.args.summon.slot_pos = rider_item_pos;
// summon interface를 사용하기 위해 casting상태로 전환시킨다.
container_edit.state.state = container.state.eReq_SummonStart;
container_edit.state.args.casting_start.slot_pos = rider_item_pos;
container_edit.casting_start = GetTickCount() + 60 * 60 * 1000; //update에서 취소되지 않도록...
container_edit.transaction_mode = container_edit.eTrans_SyncRequested;
return true;
}
// EnterField
BOOLEAN
IPlayerRiderD::SyncUpdateRidingStatus(Player* player)
{
__UNUSED(player);
const PlayerRider::Container& container = REF_RIDER_DATA(container_);
//----------------------------------------------------------------------------------------------
// call by OnEnterField
if (container.is_summoned) {
IVehicleEvent::OnEvent(player, IVehicleEvent::EnterField());
}
//----------------------------------------------------------------------------------------------
BOOLEAN need_summon = container.enter_state.request == container.enter_state.eReq_Summon &&
container.state.request == container.enter_state.eReq_SummonStart;
if(need_summon == 0)
return true;
PlayerRider::Container& container_edit = const_cast<PlayerRider::Container&>(container);
container_edit.casting_start = GetTickCount() - 60 * 1000; //casting관련해서는 반드시 성공할 수 있도록
if(!FlowControl::FCAssert(container.handler != 0))
return false;
// no notify to client - client has been do control 'standalone summon' (based on item stream info)
RiderControl result;
RiderControlData result_data;
container.handler->is_transaction_ = true;
PlayerRider::eResult ret = container.handler->Summon(container.enter_state, &result, &result_data);
assert(ret != container.handler->eResult_Fail);
if (container.is_summoned) {
IVehicleEvent::OnEvent(player, IVehicleEvent::EnterField());
};
return true;
}
// assert 'IsValidState() != false'
BOOLEAN
IPlayerRiderD::Command_SummonRelease(const IVehicleEvent& event)
{
const PlayerRider::Container& container = REF_RIDER_DATA(container_);
if(container.is_summoned == 0)
return false;
if(!FlowControl::FCAssert(container_.handler != 0))
return false;
MSG_CG_ITEM_RIDER_CONTROL_BRD msgBRD;
RiderControl cmd = { 0, };
cmd.request = cmd.eReq_SummonRelease;
cmd.args.summon.slot_pos = container.handler->item_pos_;
PlayerRider::eResult result = container_.handler->SummonRelease(cmd, &msgBRD.header, &msgBRD.data);
// event chain : on success control
if(result != container.handler->eResult_Fail) {
switch(event.event)
{
case event.eEvent_CancelByHit:
case event.eEvent_CancelByDead:
msgBRD.header.result_code = RC::RC_RIDER_SUMMON_RELEASED_BY_HIT;
break;
case event.eEvent_TimeExpired:
msgBRD.header.result_code = RC::RC_RIDER_TIME_EXPIRED;
break;
}
}
Player* player = container.player;
if(result == container.handler->eResult_1toN)
player->SendPacketAround(&msgBRD, msgBRD.GetSize(), true);
else
player->SendPacket(&msgBRD, msgBRD.GetSize());
// <- 로직이 꼬이는 것을 방지하기 위해서는 어떻게 해서든지 제거가 성공하고 싶다.
// 조건을 좀 더 검토하고, 이것이 사용되는 위치에서 문제가 발생시 다음 턴 재시도, 또는 오류 복구
// 가 이뤄질 수 있도록 고민해야 할 것이다.
if(result == container.handler->eResult_Fail) {
// NOTE: temporary log, check more detailed state
SUNLOG(eCRITICAL_LOG, _T("|[%s]|斤口=fails, ErrorCode=%d|"),
__FUNCTION__, msgBRD.header.result_code);
return false;
}
return true;
}
// (WARNING) ItemManager::_UpdateExpirationItem()에서 시간 만료에 의한 이벤트 (기간 연장 하지 않는
// 이상 단 한번)에 의해 호출된다. 다른 사례는 아직 찾지 못했다.
BOOLEAN
IPlayerRiderD::Command_ItemSync(Player* player, const SCItemSlot& changed_item)
{
// 하기 opertion이 수행되면 GetRiderAttrChangedInfo의 결과값이 달라진다. 미리 얻어논다.
nsSlot::RiderUpdateBits update_bits = changed_item.GetRiderAttrChangedInfo();
if(!FlowControl::FCAssert(update_bits.value != 0))
return false;
const PlayerRider::Container& container = REF_RIDER_DATA(container_);
BOOLEAN is_summoned_item = changed_item.IsRiderSummoned();
POSTYPE item_pos = changed_item.GetPos();
MSG_CG_ITEM_RIDER_CONTROL_BRD msgBRD;
msgBRD.header.Clear();
msgBRD.data.Clear();
if(is_summoned_item) {
assert(container.is_summoned != 0 && container.handler != 0);
container.handler->CancelOption(changed_item, update_bits);
}
msgBRD.header.request = msgBRD.header.eCmd_SyncRider;
msgBRD.header.args.sync.where = item_pos;
msgBRD.data.data_type = msgBRD.data.eDataType_ItemAction;
RiderControlData::sItemAction& item_action = msgBRD.data.item_action;
item_action.requester = (WORD)player->GetObjectKey();
item_action.update_bits = update_bits;
changed_item.CopyOut(item_action.item_slot);
// 기한 만료의 경우, 주변에 parts정보의 갱신이 필요할 지도 모른다.
if(is_summoned_item) {
player->SendPacketAround(&msgBRD, msgBRD.GetSize(), true);
}
else {
player->SendPacket(&msgBRD, msgBRD.GetSize());
}
if(is_summoned_item) {
if(update_bits.sub_rider)
Command_SummonRelease(IVehicleEvent::TimeExpired());
}
return true;
}
//--------------------------------------------------------------------------------------------------
// Battle Event
bool IPlayerRiderD::_OnEvent(const IVehicleEvent& event)
{
const PlayerRider::Container& container = REF_RIDER_DATA(container_);
// (NOTE) 추가 요청사항에 의해 eEvent_Hit는 두 가지로 구분된다.
// 소환 상태 전 캐스팅 상태 | 소환된 이후 상태
// 캐스팅 상태일 경우 캐스팅 취소
// 소환 이후는 데미지 처리 방식에 따른 변환
bool processed = false;
bool released = false;
if(event.event == event.eEvent_Hit) {
if(container.handler == 0)
return false;
else if(container.state.state == container.state.eReq_SummonStart) {
MSG_CG_ITEM_RIDER_CONTROL_BRD msgBRD;
msgBRD.Clear();
RiderControl req_info;
req_info.Clear();
req_info.request = req_info.eReq_SummonCancel;
PlayerRider& rider = *container.handler;
PlayerRider::eResult result = rider.Route(req_info, &msgBRD.header, &msgBRD.data);
if(result == rider.eResult_1toN)
container.player->SendPacketAround(&msgBRD, msgBRD.GetSize(), true);
else
container.player->SendPacket(&msgBRD, msgBRD.GetSize());
return true;
}
else if(container.is_summoned == 0)
return false;
// (WARNING) 위 조건 검사 순서 중요...
//
// process normal hit events
PlayerRider& rider = *container.handler;
const DWORD current_tick = GetTickCount();
const DWORD cacel_count_timer = rider.rider_info_->cancel_count_timer_;
processed = true;
//printf("|DEBUG-1st|Start=%d|CancelCount=%d|CancelTimeout=%u|\n",
// rider.hit_count_start_, rider.spawn_cancel_count_, rider.cancel_timeout_);
IVehicleEvent post_effect = { post_effect.eEvent_None };
if(rider.hit_count_start_ == 0) {
// event start
rider.hit_count_start_ = true;
rider.spawn_cancel_count_ = rider.rider_info_->spawn_cancel_count_;
rider.cancel_timeout_ = current_tick + cacel_count_timer;
}
if(rider.cancel_timeout_ < current_tick) {
// timeout!
if(rider.spawn_cancel_count_) { // < reset
rider.spawn_cancel_count_ = rider.rider_info_->spawn_cancel_count_;
rider.cancel_timeout_ = current_tick + cacel_count_timer;
}
else {
post_effect.event = post_effect.eEvent_CancelByHit;
}
}
else if(rider.spawn_cancel_count_ == 0) {
post_effect.event = post_effect.eEvent_CancelByHit;
}
else if(container.player->IsDead()) {
post_effect.event = post_effect.eEvent_CancelByDead;
}
//printf("|DEBUG-2nd|Start=%d|CancelCount=%d|CancelTimeout=%u|\n",
// rider.hit_count_start_, rider.spawn_cancel_count_, rider.cancel_timeout_);
if(post_effect.event == post_effect.eEvent_None) {
if(rider.spawn_cancel_count_ != 0)
--rider.spawn_cancel_count_;
}
else {
if(Command_SummonRelease(post_effect) != 0)
released = true;
}
}
else {
// other events, except 'Hit'
assert(container.is_summoned != 0 && container.handler != 0);
PlayerRider& rider = *container.handler;
switch(event.event)
{
case event.eEvent_EnterField:
rider.hit_count_start_ = 0;
rider.spawn_cancel_count_ = 0;
rider.cancel_timeout_ = 0;
processed = true;
break;
case event.eEvent_LeaveField:
break;
}
}
// if (release == true) then can't control rider
return processed;
}
//==================================================================================================
// for GM interface
BOOLEAN
PlayerRider::ChangeToInstanceCasting() const
{
const Container& container = REF_RIDER_DATA(delegator_.container_);
Container& container_edit = const_cast<Container&>(container);
container_edit.casting_start = 0;
return true;
}