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

689 lines
21 KiB
C++

#include "stdafx.h"
#include "Player.h" // internally include header 'TriggerLinkInGame.hxx'
//
#include "TriggerManagerEx.h"
#include "MovingPlanner.h"
//==================================================================================================
//
bool ns_trigger::TriggerPlayerLinkInGame::IsValidState() const
{
GameField* game_field = (player_ == NULL) ? NULL : player_->GetField();
if (game_field == NULL) {
return false;
};
eZONE_STATE zone_state = player_->GetGameZoneState();
if ((zone_state & ePRS_AT_ZONE) != ePRS_AT_ZONE) {
return false;
};
ePLAYER_BEHAVE_STATE behaveState = player_->GetBehave();
if (behaveState != PLAYER_BEHAVE_IDLE_STATE) {
return false;
};
return true;
}
//==================================================================================================
struct ns_trigger::TriggerPlayerLinkInGameSlots::PoolNode
: public util::__LList<TriggerPlayerLinkInGameSlots::PoolNode>
{
ns_trigger::TriggerPlayerLinkInGame link;
};
struct ns_trigger::TriggerPlayerLinkInGameSlots::Pool
{
typedef ns_trigger::TriggerPlayerLinkInGameSlots::PoolNode Node;
Pool()
{
util::LList::Init(&free_root_);
}
~Pool()
{
while (util::LList::IsEmpty(&free_root_) == false)
{
Node* it = static_cast<Node*>(free_root_.next);
util::LList::Delete(it);
TAllocDelete(Node, it);
}
}
Node* Alloc(Player* player)
{
Node* node = NULL;
if (util::LList::IsEmpty(&free_root_)) {
node = TAllocNew(Node);
util::LList::Init(node);
}
else {
node = static_cast<Node*>(free_root_.next);
util::LList::Delete(node);
}
//util::LList::AddPrev(node, &alloc_root_);
new (&node->link) TriggerPlayerLinkInGame(player);
return node;
}
void Free(Node* node)
{
node->link.Delete();
util::LList::Delete(node);
util::LList::AddPrev(node, &free_root_);
}
Node free_root_;
__DISABLE_COPY(Pool);
};
ns_trigger::TriggerPlayerLinkInGameSlots::Pool*
ns_trigger::TriggerPlayerLinkInGameSlots::pool_static_ = NULL;
//==================================================================================================
// (CHANGES) (f100614.4L) add a slot type for a trigger area linker to support a multi-area link
ns_trigger::TriggerPlayerLinkInGameSlots::TriggerPlayerLinkInGameSlots(Player* player)
{
player_ = player;
util::LList::Init(&linked_areas_);
}
ns_trigger::TriggerPlayerLinkInGameSlots::~TriggerPlayerLinkInGameSlots()
{
Pool::Node* it = linked_areas_.next;
for (; it != &linked_areas_; )
{
Pool::Node* node = it;
it = it->next;
pool_static_->Free(node);
}
}
ns_trigger::TriggerPlayerLinkInGame*
ns_trigger::TriggerPlayerLinkInGameSlots::GetAreaLink(int area_id)
{
if (util::LList::IsEmpty(&linked_areas_)) {
return NULL;
}
Pool::Node* it = linked_areas_.next;
for (; it != &linked_areas_; it = it->next)
{
if (it->link.area_id_ == area_id && util::LList::IsEmpty(&it->link) == false) {
return &it->link;
}
}
return NULL;
}
ns_trigger::TriggerPlayerLinkInGame*
ns_trigger::TriggerPlayerLinkInGameSlots::AllocEmptySlot()
{
static TriggerPlayerLinkInGameSlots::Pool pool_static;
pool_static_ = &pool_static;
Pool::Node* node = pool_static_->Alloc(player_);
util::LList::AddPrev(node, &linked_areas_);
return &node->link;
}
void ns_trigger::TriggerPlayerLinkInGameSlots::FreeSlot(TriggerPlayerLinkInGame* area_link)
{
assert(util::LList::IsEmpty(&linked_areas_) == false);
Pool::Node* node = CONTAINING_RECORD(area_link, Pool::Node, link);
pool_static_->Free(node);
}
// execute a replaced delete operation if trigger_manager is equal to null.
void ns_trigger::TriggerPlayerLinkInGameSlots::Disconnect(TriggerManagerEx* trigger_manager)
{
if (util::LList::IsEmpty(&linked_areas_)) {
return;
}
Pool::Node* it = linked_areas_.next;
for (; it != &linked_areas_; )
{
TriggerPlayerLinkInGame* link = &it->link;
if (trigger_manager) {
trigger_manager->Disconnected(link);
}
else {
link->Delete();
}
Pool::Node* free_node = it;
it = it->next;
pool_static_->Free(free_node);
}
}
//==================================================================================================
//==================================================================================================
//==================================================================================================
//==================================================================================================
//
bool ns_trigger::TriggerNpcLinkInGame::IsValidState() const
{
GameField* game_field = (npc_ == NULL) ? NULL : npc_->GetField();
if (game_field == NULL) {
return false;
};
if (npc_->IsDeleteReserved()) {
return false;
};
eZONE_STATE zone_state = npc_->GetGameZoneState();
if ((zone_state & ePRS_AT_ZONE) != ePRS_AT_ZONE) {
return false;
};
return true;
}
//==================================================================================================
struct ns_trigger::TriggerNpcLinkInGameSlots::PoolNode
: public util::__LList<TriggerNpcLinkInGameSlots::PoolNode>
{
ns_trigger::TriggerNpcLinkInGame link;
};
struct ns_trigger::TriggerNpcLinkInGameSlots::Pool
{
typedef ns_trigger::TriggerNpcLinkInGameSlots::PoolNode Node;
Pool()
{
util::LList::Init(&free_root_);
}
~Pool()
{
while (util::LList::IsEmpty(&free_root_) == false)
{
Node* it = static_cast<Node*>(free_root_.next);
util::LList::Delete(it);
TAllocDelete(Node, it);
}
}
Node* Alloc(NPC* npc)
{
Node* node = NULL;
if (util::LList::IsEmpty(&free_root_)) {
node = TAllocNew(Node);
util::LList::Init(node);
}
else {
node = static_cast<Node*>(free_root_.next);
util::LList::Delete(node);
}
//util::LList::AddPrev(node, &alloc_root_);
new (&node->link) TriggerNpcLinkInGame(npc);
return node;
}
void Free(Node* node)
{
node->link.Delete();
util::LList::Delete(node);
util::LList::AddPrev(node, &free_root_);
}
Node free_root_;
__DISABLE_COPY(Pool);
};
ns_trigger::TriggerNpcLinkInGameSlots::Pool*
ns_trigger::TriggerNpcLinkInGameSlots::pool_static_ = NULL;
//==================================================================================================
// CHANGES: f110311.4L add a slot type for a trigger area linker to support a multi-area link
ns_trigger::TriggerNpcLinkInGameSlots::TriggerNpcLinkInGameSlots(NPC* npc)
{
npc_ = npc;
util::LList::Init(&linked_areas_);
}
ns_trigger::TriggerNpcLinkInGameSlots::~TriggerNpcLinkInGameSlots()
{
Pool::Node* it = linked_areas_.next;
for (; it != &linked_areas_; )
{
Pool::Node* node = it;
it = it->next;
pool_static_->Free(node);
}
}
ns_trigger::TriggerNpcLinkInGame*
ns_trigger::TriggerNpcLinkInGameSlots::GetAreaLink(int area_id)
{
if (util::LList::IsEmpty(&linked_areas_)) {
return NULL;
}
Pool::Node* it = linked_areas_.next;
for (; it != &linked_areas_; it = it->next)
{
if (it->link.area_id_ == area_id && util::LList::IsEmpty(&it->link) == false) {
return &it->link;
}
}
return NULL;
}
ns_trigger::TriggerNpcLinkInGame*
ns_trigger::TriggerNpcLinkInGameSlots::AllocEmptySlot()
{
static TriggerNpcLinkInGameSlots::Pool pool_static;
pool_static_ = &pool_static;
Pool::Node* node = pool_static_->Alloc(npc_);
util::LList::AddPrev(node, &linked_areas_);
return &node->link;
}
void ns_trigger::TriggerNpcLinkInGameSlots::FreeSlot(TriggerNpcLinkInGame* area_link)
{
assert(util::LList::IsEmpty(&linked_areas_) == false);
Pool::Node* node = CONTAINING_RECORD(area_link, Pool::Node, link);
pool_static_->Free(node);
}
// execute a replaced delete operation if trigger_manager is equal to null.
void ns_trigger::TriggerNpcLinkInGameSlots::Disconnect(TriggerManagerEx* trigger_manager)
{
if (util::LList::IsEmpty(&linked_areas_)) {
return;
}
#ifdef _NA_002098_20110307_ADD_NPC_CONTROL_TRIGGER_BASE
// temporary block
Pool::Node* it = linked_areas_.next;
for (; it != &linked_areas_; )
{
TriggerNpcLinkInGame* link = &it->link;
if (trigger_manager) {
TriggerAreaManagerForNpc* trigger_area_manager =
const_cast<TriggerAreaManagerForNpc*>(trigger_manager->GetAreaManagerForNpc());
TriggerAreaForNpc::ForceLeave(trigger_area_manager, link);
}
else {
link->Delete();
}
Pool::Node* free_node = it;
it = it->next;
pool_static_->Free(free_node);
}
#endif
}
//==================================================================================================
//==================================================================================================
//==================================================================================================
ns_trigger::TriggerNpcLinkMatchTable::AreaID_Node::AreaID_Node()
{
util::LList::Init(this);
}
ns_trigger::TriggerNpcLinkMatchTable::AreaID_Node::~AreaID_Node()
{
util::LList::Delete(this);
}
void ns_trigger::TriggerNpcLinkMatchTable::AreaID_Node::MakeToRootNode()
{
util::LList::Init(this);
area_id = -1;
special_area = NULL;
}
//==================================================================================================
ns_trigger::TriggerNpcLinkMatchTable::TriggerNpcLinkMatchTable()
: game_field_(NULL)
{
}
ns_trigger::TriggerNpcLinkMatchTable::~TriggerNpcLinkMatchTable()
{
area_event_containing_units_.clear();
area_event_containing_monster_code_.clear();
//
FOREACH_CONTAINER(TILE_AREA_MATCHING_TABLE::value_type& node, tile_area_matching_table_,
TILE_AREA_MATCHING_TABLE)
{
AreaID_Node* const root = &node.second;
AreaID_Node* it = root->next;
while (it != root)
{
// for safe iteration
AreaID_Node* temp = it->next;
TAllocDelete(AreaID_Node, it);
it = temp;
}
};
tile_area_matching_table_.clear();
}
#ifdef _NA_002098_20110307_ADD_NPC_CONTROL_TRIGGER_BASE
void ns_trigger::TriggerNpcLinkMatchTable::Init(GameField* game_field)
{
game_field_ = game_field;
area_event_containing_units_.clear();
area_event_containing_monster_code_.clear();
tile_area_matching_table_.clear();
}
bool ns_trigger::TriggerNpcLinkMatchTable::IsExistTriggerAreaEvent() const
{
if (game_field_ == NULL) {
return false;
};
if (area_event_containing_units_.empty() &&
area_event_containing_monster_code_.empty()) {
return false;
};
if (tile_area_matching_table_.empty()) {
return false;
}
return true;
}
void ns_trigger::TriggerNpcLinkMatchTable::CreateEventNpcTriggerAreaEvents(int area_id)
{
if (game_field_ == NULL) {
return;
};
CWorldBase* const world_base = game_field_->GetFieldInfo()->GetWorldBase();
const int number_of_special_areas = world_base->GetNumberOfSpecialArea();
WzSpecialArea* special_area = NULL;
if (!FlowControl::FCAssert(area_id >= 0 && area_id < number_of_special_areas &&
(special_area = world_base->GetSpecialAreaByIndex(area_id)) != NULL))
{
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|ÐÅÏ¢=can't find special area (index = %d)"),
area_id);
return;
}
const WzBoundingVolume& bounding_volume = special_area->m_wbvRegion;
if (bounding_volume.m_eBvType != BVT_PATH_TILE_INDEX) {
return; //
};
nsAI::MovingPlanner* const moving_planner = game_field_->GetMovingPlanner();
const BvPathTileIndex& path_tiles = bounding_volume.m_BvPathTileIndex;
// apply tile event tag in the game field
const DWORD* tile_it = path_tiles.m_pdwData,
* const tile_end = &path_tiles.m_pdwData[path_tiles.m_nCount];
for ( ; tile_it != tile_end; ++tile_it)
{
const int tile_no = *tile_it;
//const nsAI::TileEvent* tile_event;
const nsAI::eCharEvent char_event = nsAI::eCharEvent_ExistNpcTileEvent;
//
const nsAI::PlanCmd::NpcAreaEvent args(nsAI::PlanCmd::eNpcAreaEvent_Regist,
tile_no, char_event);
moving_planner->PathPlan_Command(args);
//
//
TILE_AREA_MATCHING_TABLE::iterator exist_it = tile_area_matching_table_.find(tile_no);
const bool first_entry = (exist_it == tile_area_matching_table_.end());
AreaID_Node* const root = &tile_area_matching_table_[tile_no];
if (first_entry) {
root->MakeToRootNode();
};
;{
const AreaID_Node* it = root->next;
for ( ; it != root; it = it->next)
{
if (it->area_id == area_id) {
break;
};
}
if (it == root)
{ // not found
AreaID_Node* const new_item = TAllocNew(AreaID_Node);
new_item->area_id = area_id;
new_item->special_area = special_area;
util::LList::AddPrev(new_item, root);
}
};
}
}
void ns_trigger::TriggerNpcLinkMatchTable::AddEventNpcTriggerAreaEvents(
int area_id, CODETYPE unit_id)
{
CreateEventNpcTriggerAreaEvents(area_id);
//-------------------------------------------------------
;{
AREA_EVENT_CONTAINING_UNITS::const_iterator exist =
area_event_containing_units_.find(unit_id);
if (exist == area_event_containing_units_.end()) {
area_event_containing_units_.insert(unit_id);
}
};
}
void ns_trigger::TriggerNpcLinkMatchTable::AddEventMonsterTriggerAreaEvents(
int area_id, CODETYPE monster_code)
{
CreateEventNpcTriggerAreaEvents(area_id);
//-------------------------------------------------------
;{
AREA_EVENT_CONTAINING_UNITS::const_iterator exist =
area_event_containing_monster_code_.find(monster_code);
if (exist == area_event_containing_monster_code_.end()) {
area_event_containing_monster_code_.insert(monster_code);
}
};
}
//==================================================================================================
namespace ns_trigger {
;
struct TriggerAreaEventSelector
{
TriggerAreaEventSelector(NPC* input_npc);
void ProcessEvent();
//
NPC* npc;
//
int number_of_prev_areas;
int number_of_post_areas;
int number_of_leave_events;
int number_of_enter_events;
CODETYPE prev_areas[16];
CODETYPE post_areas[16];
CODETYPE leave_events[16];
CODETYPE enter_events[16];
};
}; //end of namespace
//
ns_trigger::TriggerAreaEventSelector::TriggerAreaEventSelector(NPC* input_npc)
{
ZeroMemory(this, sizeof(*this));
npc = input_npc;
}
void ns_trigger::TriggerAreaEventSelector::ProcessEvent()
{
;{
CODETYPE* dest_it = leave_events;
//
CODETYPE* left_it = prev_areas;
const CODETYPE* const left_end = &prev_areas[_countof(prev_areas)];
//
for ( ; left_it != left_end && *left_it; ++left_it)
{
bool found = false;
for (CODETYPE* right_it = post_areas;
right_it != &post_areas[_countof(post_areas)] && *right_it;
++right_it)
{
if ((*right_it) == (*left_it)) {
found = true;
break;
}
}
if (found == false) {
*dest_it++ = *left_it;
}
}
};
;{
CODETYPE* dest_it = enter_events;
//
CODETYPE* left_it = post_areas;
const CODETYPE* const left_end = &post_areas[_countof(post_areas)];
//
const CODETYPE* const right_end = &prev_areas[_countof(prev_areas)];
for ( ; left_it != left_end && *left_it; ++left_it)
{
bool found = false;
for (CODETYPE* right_it = prev_areas;
right_it != right_end && *right_it;
++right_it)
{
if ((*right_it) == (*left_it)) {
found = true;
break;
}
}
if (found == false) {
*dest_it++ = *left_it;
}
}
};
//
GameField* const game_field = npc->GetField();
TriggerManagerEx* const trigger_manager = game_field->GetTriggerManager();
ns_trigger::TriggerNpcLinkInGameSlots* const trigger_links = npc->GetTriggerLinks();
;{
TRIGGER_NPC_LEAVE_AREA msg;
msg.object_key = npc->GetObjectKey();
//msg.area_id =
//msg.link =
CODETYPE* leave_it = leave_events;
const CODETYPE* const leave_end = &leave_events[_countof(leave_events)];
for ( ; leave_it != leave_end && *leave_it; ++leave_it)
{
msg.area_id = (*leave_it);
msg.link = trigger_links->GetAreaLink(msg.area_id);
if (msg.link == NULL) {
continue;
}
trigger_manager->OnLeaveAreaForNpc(&msg);
trigger_links->FreeSlot(
static_cast<ns_trigger::TriggerNpcLinkInGame*>(msg.link));
}
};
;{
TRIGGER_NPC_ENTER_AREA msg;
msg.object_key = npc->GetObjectKey();
//msg.area_id =
//msg.link =
CODETYPE* enter_it = enter_events;
const CODETYPE* const enter_end = &enter_events[_countof(enter_events)];
for ( ; enter_it != enter_end && *enter_it; ++enter_it)
{
msg.area_id = (*enter_it);
msg.link = trigger_links->AllocEmptySlot();
if (trigger_manager->OnEnterAreaForNpc(&msg) == false) {
trigger_links->FreeSlot(
static_cast<ns_trigger::TriggerNpcLinkInGame*>(msg.link));
};
}
};
};
//==================================================================================================
//
bool ns_trigger::TriggerNpcLinkMatchTable::OnChangeTile(
int prev_tile_no, int new_tile_no, NPC* npc) const
{
if (prev_tile_no == new_tile_no) {
return false;
};
// NOTE: assumed area_event_containing_units_ include the inputed npc
TriggerAreaEventSelector event_selector(npc);
//
TILE_AREA_MATCHING_TABLE::const_iterator prev_it = tile_area_matching_table_.find(prev_tile_no);
if (prev_it != tile_area_matching_table_.end())
{
const AreaID_Node* const root = &prev_it->second;
AreaID_Node* area_it = root->next;
CODETYPE* dest_it = event_selector.prev_areas;
const CODETYPE* const dest_end = &event_selector.prev_areas[
_countof(event_selector.prev_areas)];
for ( ; area_it != root; area_it = area_it->next)
{
*dest_it++ = area_it->area_id;
if (dest_it == dest_end)
{
ASSERT(!"too many duplicated areas");
break;
}
}
event_selector.number_of_prev_areas =
static_cast<int>(dest_it - event_selector.prev_areas);
};
TILE_AREA_MATCHING_TABLE::const_iterator post_it = tile_area_matching_table_.find(new_tile_no);
if (post_it != tile_area_matching_table_.end())
{
const AreaID_Node* const root = &post_it->second;
AreaID_Node* area_it = root->next;
CODETYPE* dest_it = event_selector.post_areas;
const CODETYPE* const dest_end = &event_selector.post_areas[
_countof(event_selector.post_areas)];
for ( ; area_it != root; area_it = area_it->next)
{
*dest_it++ = area_it->area_id;
if (dest_it == dest_end)
{
ASSERT(!"too many duplicated areas");
break;
}
}
event_selector.number_of_post_areas =
static_cast<int>(dest_it - event_selector.post_areas);
};
//
event_selector.ProcessEvent(); //enter & leave trigger events
//
return true;
}
// the npc area event check interface
bool ns_trigger::TriggerNpcLinkMatchTable::CheckExistAreaEvent(CODETYPE unit_id) const
{
AREA_EVENT_CONTAINING_UNITS::const_iterator exist =
area_event_containing_units_.find(unit_id);
if (exist == area_event_containing_units_.end()) {
return false;
}
return true;
}
bool ns_trigger::TriggerNpcLinkMatchTable::CheckExistMonsterAreaEvent( CODETYPE monster_code ) const
{
AREA_EVENT_CONTAINING_UNITS::const_iterator exist =
area_event_containing_monster_code_.find(monster_code);
if (exist == area_event_containing_monster_code_.end()) {
return false;
}
return true;
}
#endif //
//==================================================================================================