diff --git a/src/xrGame/Actor.h b/src/xrGame/Actor.h index 9a3df4a158e..239aed7d8af 100644 --- a/src/xrGame/Actor.h +++ b/src/xrGame/Actor.h @@ -124,6 +124,7 @@ class CActor : public CEntityAlive, protected: virtual void AddEncyclopediaArticle(const CInfoPortion* info_portion) const; + virtual void AddGameTask(const CInfoPortion* info_portion) const; struct SDefNewsMsg { diff --git a/src/xrGame/GameTask.cpp b/src/xrGame/GameTask.cpp index 0a860c690a9..a2b5bd39e7e 100644 --- a/src/xrGame/GameTask.cpp +++ b/src/xrGame/GameTask.cpp @@ -13,24 +13,248 @@ #include "script_game_object.h" #include "ai_space.h" #include "alife_object_registry.h" +#include "alife_registry_wrappers.h" #include "alife_simulator.h" #include "alife_story_registry.h" #include "game_object_space.h" #include "Common/object_broker.h" #include "xrUICore/XML/UITextureMaster.h" -SGameTaskObjective::SGameTaskObjective() - : m_task_state(eTaskStateDummy), - m_task_type(eTaskTypeDummy) {} +CUIXml* g_gameTaskXml = nullptr; -SGameTaskObjective::SGameTaskObjective(CGameTask* parent, int idx) +ALife::_STORY_ID story_id(cpcstr story_id) +{ + using namespace luabind; + const int res = object_cast( + object( + globals(GEnv.ScriptEngine->lua()) + ["story_ids"] + ) + [story_id] + ); + return ALife::_STORY_ID(res); +} + +u16 storyId2GameId(ALife::_STORY_ID id) +{ + if (ai().get_alife()) + { + CSE_ALifeDynamicObject* so = ai().alife().story_objects().object(id, true); + return (so) ? so->ID : u16(-1); + } + + const u32 cnt = Level().Objects.o_count(); + for (u32 it = 0; it < cnt; ++it) + { + IGameObject* O = Level().Objects.o_get_by_iterator(it); + CGameObject* GO = smart_cast(O); + if (GO->story_id() == id) + return GO->ID(); + } + return u16(-1); +} + +SGameTaskObjective::SGameTaskObjective(CGameTask* parent, TASK_OBJECTIVE_ID idx) : m_parent(parent), m_task_state(eTaskStateDummy), m_task_type(eTaskTypeDummy), m_idx(idx) {} CGameTask::CGameTask() - : SGameTaskObjective(this, 0) {} + : SGameTaskObjective(this, ROOT_TASK_OBJECTIVE) {} + +void CGameTask::Load(const TASK_ID& id) +{ + m_ID = id; + static bool successfulLoading = false; + if (!g_gameTaskXml) + { + g_gameTaskXml = new CUIXml(); + successfulLoading = g_gameTaskXml->Load(CONFIG_PATH, "gameplay", "game_tasks.xml", false); + } + if (!successfulLoading) + { + Msg("Trying to load task [%s], but failed to load gameplay/game_tasks.xml", m_ID.c_str()); + return; + } + + const XML_NODE task_node = g_gameTaskXml->NavigateToNodeWithAttribute("game_task", "id", *id); + + THROW3(task_node, "game task id = ", id.c_str()); + g_gameTaskXml->SetLocalRoot(task_node); + m_Title = g_gameTaskXml->Read(g_gameTaskXml->GetLocalRoot(), "title", 0, nullptr); + m_priority = g_gameTaskXml->ReadAttribInt(g_gameTaskXml->GetLocalRoot(), "prio", -1); + + #ifdef DEBUG + if (m_priority == u32(-1)) + { + Msg("Game Task [%s] has no priority", id.c_str()); + } +#endif + + const int tag_num = g_gameTaskXml->GetNodesNum(g_gameTaskXml->GetLocalRoot(), "objective"); + m_Objectives.clear(); + for (int i = 0; i < tag_num; i++) + { + const XML_NODE l_root = g_gameTaskXml->NavigateToNode("objective", i); + g_gameTaskXml->SetLocalRoot(l_root); + + SGameTaskObjective& objective = (i == ROOT_TASK_OBJECTIVE) + ? *this + : m_Objectives.emplace_back(this, i); + + //. + pcstr tag_text = g_gameTaskXml->Read(l_root, "text", 0, nullptr); + objective.m_Description = tag_text; + + //. + tag_text = g_gameTaskXml->Read(l_root, "article", 0, nullptr); + if (tag_text) + objective.m_article_id = tag_text; + + //. + tag_text = g_gameTaskXml->ReadAttrib(l_root, "key", nullptr); + if (tag_text) + objective.m_article_key = tag_text; + + //. + if (i == ROOT_TASK_OBJECTIVE) + { + objective.m_icon_texture_name = g_gameTaskXml->Read(g_gameTaskXml->GetLocalRoot(), "icon", 0, nullptr); + if (objective.m_icon_texture_name.size() && + 0 != xr_stricmp(*objective.m_icon_texture_name, "ui\\ui_icons_task")) + { + objective.m_icon_rect = CUITextureMaster::GetTextureRect(*objective.m_icon_texture_name); + objective.m_icon_rect.rb.sub(objective.m_icon_rect.rb, objective.m_icon_rect.lt); + objective.m_icon_texture_name = CUITextureMaster::GetTextureFileName(*objective.m_icon_texture_name); + } + else if (objective.m_icon_texture_name.size()) + { + objective.m_icon_rect.x1 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "x"); + objective.m_icon_rect.y1 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "y"); + objective.m_icon_rect.x2 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "width"); + objective.m_icon_rect.y2 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "height"); + } + } + //. + objective.m_map_location = g_gameTaskXml->Read(l_root, "map_location_type", 0, nullptr); + + cpcstr object_story_id = g_gameTaskXml->Read(l_root, "object_story_id", 0, nullptr); + + //* + objective.m_def_location_enabled = !g_gameTaskXml->ReadInt(l_root, "map_location_hidden", 0, 0); + + const bool b1 = (0 == objective.m_map_location.size()); + const bool b2 = (nullptr == object_story_id); + VERIFY3(b1 == b2, "check [map_location_type] and [object_story_id] fields in objective definition for: ", + objective.m_Description.c_str()); + + //. + objective.m_map_object_id = u16(-1); + + //. + objective.m_map_hint = g_gameTaskXml->ReadAttrib(l_root, "map_location_type", 0, "hint", nullptr); + + if (object_story_id) + { + const ALife::_STORY_ID _sid = story_id(object_story_id); + objective.m_map_object_id = storyId2GameId(_sid); + } + + //------infoportion_complete + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "infoportion_complete"); + objective.m_completeInfos.resize(info_num); + + for (int j = 0; j < info_num; ++j) + { + objective.m_completeInfos[j] = g_gameTaskXml->Read(l_root, "infoportion_complete", j, nullptr); + } + } + + //------infoportion_fail + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "infoportion_fail"); + objective.m_failInfos.resize(info_num); + + for (int j = 0; j < info_num; ++j) + { + objective.m_failInfos[j] = g_gameTaskXml->Read(l_root, "infoportion_fail", j, nullptr); + } + } + + //------infoportion_set_complete + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "infoportion_set_complete"); + objective.m_infos_on_complete.resize(info_num); + for (int j = 0; j < info_num; ++j) + { + objective.m_infos_on_complete[j] = g_gameTaskXml->Read(l_root, "infoportion_set_complete", j, nullptr); + } + } + + //------infoportion_set_fail + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "infoportion_set_fail"); + objective.m_infos_on_fail.resize(info_num); + for (int j = 0; j < info_num; ++j) + { + objective.m_infos_on_fail[j] = g_gameTaskXml->Read(l_root, "infoportion_set_fail", j, nullptr); + } + } + + //------function_complete + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "function_complete"); + objective.m_complete_lua_functions.resize(info_num); + for (int j = 0; j < info_num; ++j) + { + cpcstr str = g_gameTaskXml->Read(l_root, "function_complete", j, nullptr); + const bool functor_exists = GEnv.ScriptEngine->functor(str, objective.m_complete_lua_functions[j]); + THROW3(functor_exists, "Cannot find script function described in task objective ", str); + } + } + + //------function_fail + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "function_fail"); + objective.m_fail_lua_functions.resize(info_num); + for (int j = 0; j < info_num; ++j) + { + cpcstr str = g_gameTaskXml->Read(l_root, "function_fail", j, nullptr); + const bool functor_exists = GEnv.ScriptEngine->functor(str, objective.m_fail_lua_functions[j]); + THROW3(functor_exists, "Cannot find script function described in task objective ", str); + } + } + + //------function_on_complete + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "function_call_complete"); + objective.m_lua_functions_on_complete.resize(info_num); + for (int i = 0; i < info_num; ++i) + { + cpcstr str = g_gameTaskXml->Read(l_root, "function_call_complete", i, nullptr); + const bool functor_exists = GEnv.ScriptEngine->functor(str, objective.m_lua_functions_on_complete[i]); + THROW3(functor_exists, "Cannot find script function described in task objective ", str); + } + } + + //------function_on_fail + { + const int info_num = g_gameTaskXml->GetNodesNum(l_root, "function_call_fail"); + objective.m_lua_functions_on_fail.resize(info_num); + for (int j = 0; j < info_num; ++j) + { + cpcstr str = g_gameTaskXml->Read(l_root, "function_call_fail", j, nullptr); + const bool functor_exists = GEnv.ScriptEngine->functor(str, objective.m_lua_functions_on_fail[j]); + THROW3(functor_exists, "Cannot find script function described in task objective ", str); + } + } + + g_gameTaskXml->SetLocalRoot(task_node); + } + g_gameTaskXml->SetLocalRoot(g_gameTaskXml->GetRoot()); +} void SGameTaskObjective::SetTaskState(ETaskState state) { @@ -54,14 +278,117 @@ void SGameTaskObjective::SetTaskState(ETaskState state) ChangeStateCallback(); } +TASK_OBJECTIVE_ID CGameTask::ActiveObjectiveIdx() const +{ + return m_active_objective; +} + +SGameTaskObjective* CGameTask::ActiveObjective() +{ + if (m_active_objective == NO_TASK_OBJECTIVE) + return nullptr; + + return &Objective(m_active_objective); +} + +SGameTaskObjective& CGameTask::Objective(TASK_OBJECTIVE_ID idx) +{ + if (idx == ROOT_TASK_OBJECTIVE) + return *this; + return m_Objectives[idx - 1]; +} + +const SGameTaskObjective& CGameTask::Objective(TASK_OBJECTIVE_ID idx) const +{ + if (idx == ROOT_TASK_OBJECTIVE) + return *this; + return m_Objectives[idx - 1]; +} + +ETaskState CGameTask::ObjectiveState(TASK_OBJECTIVE_ID idx) const +{ + if (idx == NO_TASK_OBJECTIVE) + return GetTaskState(); + return Objective(idx).GetTaskState(); +} + +void CGameTask::SetActiveObjective(TASK_OBJECTIVE_ID idx) +{ + VERIFY2(idx != 0, "Objective ID should not be 0."); + m_active_objective = idx; +} + +TASK_OBJECTIVE_ID CGameTask::GetObjectivesCount() const +{ + return m_Objectives.size() + 1; // plus task itself +} + +void CGameTask::SetTaskState(ETaskState state, TASK_OBJECTIVE_ID objective_id) +{ + if (objective_id == NO_TASK_OBJECTIVE) + { + SetTaskState(state); + return; + } + if (objective_id != ROOT_TASK_OBJECTIVE) + { + Objective(objective_id).SetTaskState(state); + return; + } + // Set state for task and all sub-tasks + if (GetTaskState() == eTaskStateInProgress) + SetTaskState(state); + for (SGameTaskObjective& objective : m_Objectives) + { + if (objective.GetTaskState() == eTaskStateInProgress) + objective.SetTaskState(state); + } +} + +bool CGameTask::HasObjectiveInProgress() const +{ + for (const SGameTaskObjective& objective : m_Objectives) + { + if (objective.GetTaskState() == eTaskStateInProgress) + return true; + } + return false; +} + void CGameTask::OnArrived() { m_task_state = eTaskStateInProgress; m_read = false; + FillEncyclopedia(); CreateMapLocation(false); } +void CGameTask::FillEncyclopedia() const +{ + ARTICLE_VECTOR& article_vector = Actor()->encyclopedia_registry->registry().objects(); + for (const SGameTaskObjective& obj : m_Objectives) + { + if (!obj.m_article_id.size()) + continue; + + if (article_vector.end() != std::find_if(article_vector.begin(), article_vector.end(), FindArticleByIDPred(obj.m_article_id))) + continue; + + CEncyclopediaArticle article; + article.Load(obj.m_article_id); + article_vector.emplace_back(obj.m_article_id, Level().GetGameTime(), article.data()->articleType); + } +} + +CMapLocation* CGameTask::LinkedMapLocation() +{ + if (m_active_objective == NO_TASK_OBJECTIVE) + return m_linked_map_location; + + return Objective(m_active_objective).LinkedMapLocation(); +} + void SGameTaskObjective::CreateMapLocation(bool on_load) { if (m_map_object_id == u16(-1) || m_map_location.size() == 0) @@ -138,6 +465,11 @@ void SGameTaskObjective::ChangeStateCallback() void CGameTask::ChangeStateCallback() { + if (!m_Objectives.empty()) + { + SGameTaskObjective::ChangeStateCallback(); + return; + } Actor()->callback(GameObject::eTaskStateChange)(this, GetTaskState()); } @@ -228,9 +560,15 @@ void SGameTaskObjective::save(IWriter& stream) save_data(m_Title, stream); save_data(m_Description, stream); + save_data(m_article_id, stream); + save_data(m_article_key, stream); + save_data(m_pScriptHelper, stream); + save_data(m_icon_rect, stream); save_data(m_icon_texture_name, stream); + save_data(m_def_location_enabled, stream); + save_data(m_map_hint, stream); save_data(m_map_location, stream); save_data(m_map_object_id, stream); @@ -250,9 +588,15 @@ void SGameTaskObjective::load(IReader& stream) load_data(m_Title, stream); load_data(m_Description, stream); + load_data(m_article_id, stream); + load_data(m_article_key, stream); + load_data(m_pScriptHelper, stream); + load_data(m_icon_rect, stream); load_data(m_icon_texture_name, stream); + load_data(m_def_location_enabled, stream); + load_data(m_map_hint, stream); load_data(m_map_location, stream); load_data(m_map_object_id, stream); @@ -275,12 +619,11 @@ void CGameTask::load(IReader& stream) CreateMapLocation(true); } -void SGameTaskObjective::CommitScriptHelperContents() +void SGameTaskObjective::SetIconName_script(pcstr tex) { - m_pScriptHelper.init_functors(m_pScriptHelper.m_s_complete_lua_functions, m_complete_lua_functions); - m_pScriptHelper.init_functors(m_pScriptHelper.m_s_fail_lua_functions, m_fail_lua_functions); - m_pScriptHelper.init_functors(m_pScriptHelper.m_s_lua_functions_on_complete, m_lua_functions_on_complete); - m_pScriptHelper.init_functors(m_pScriptHelper.m_s_lua_functions_on_fail, m_lua_functions_on_fail); + m_icon_texture_name = tex; + m_icon_rect = CUITextureMaster::GetTextureRect(m_icon_texture_name.c_str()); + m_icon_rect.rb.sub(m_icon_rect.rb, m_icon_rect.lt); } void SGameTaskObjective::AddCompleteInfo_script(pcstr str) { m_completeInfos.emplace_back(str); } @@ -295,6 +638,14 @@ void SGameTaskObjective::AddFailFunc_script(pcstr str) { m_pScriptHelper.m_s_fai void SGameTaskObjective::AddOnFailInfo_script(pcstr str) { m_infos_on_fail.emplace_back(str); } void SGameTaskObjective::AddOnFailFunc_script(pcstr str) { m_pScriptHelper.m_s_lua_functions_on_fail.emplace_back(str); } +void SGameTaskObjective::CommitScriptHelperContents() +{ + m_pScriptHelper.init_functors(m_pScriptHelper.m_s_complete_lua_functions, m_complete_lua_functions); + m_pScriptHelper.init_functors(m_pScriptHelper.m_s_fail_lua_functions, m_fail_lua_functions); + m_pScriptHelper.init_functors(m_pScriptHelper.m_s_lua_functions_on_complete, m_lua_functions_on_complete); + m_pScriptHelper.init_functors(m_pScriptHelper.m_s_lua_functions_on_fail, m_lua_functions_on_fail); +} + void SScriptTaskHelper::init_functors(xr_vector& v_src, task_state_functors& v_dest) { auto it = v_src.begin(); diff --git a/src/xrGame/GameTask.h b/src/xrGame/GameTask.h index c45d26640d8..800d13803f7 100644 --- a/src/xrGame/GameTask.h +++ b/src/xrGame/GameTask.h @@ -36,6 +36,7 @@ class SScriptTaskHelper : public ISerializable class SGameTaskObjective : public ISerializable { friend struct SGameTaskKey; + friend class CGameTask; friend class CGameTaskManager; protected: @@ -45,17 +46,23 @@ class SGameTaskObjective : public ISerializable SScriptTaskHelper m_pScriptHelper; public: - int m_idx{}; + TASK_OBJECTIVE_ID m_idx; shared_str m_Title; shared_str m_Description; + // encyclopedia + shared_str m_article_id; + shared_str m_article_key; + // icon + Frect m_icon_rect; shared_str m_icon_texture_name; // map shared_str m_map_hint; shared_str m_map_location; u16 m_map_object_id{}; + bool m_def_location_enabled{}; CMapLocation* m_linked_map_location{}; // timing @@ -79,8 +86,7 @@ class SGameTaskObjective : public ISerializable task_state_functors m_lua_functions_on_fail; public: - SGameTaskObjective(); - SGameTaskObjective(CGameTask* parent, int idx); + SGameTaskObjective(CGameTask* parent, TASK_OBJECTIVE_ID idx); CGameTask* GetParent() const { return m_parent; } @@ -88,7 +94,7 @@ class SGameTaskObjective : public ISerializable auto GetTaskState() const { return m_task_state; } auto GetTaskType() const { return m_task_type; } - auto LinkedMapLocation() const { return m_linked_map_location; } + virtual CMapLocation* LinkedMapLocation() { return m_linked_map_location; } ETaskState UpdateState(); @@ -113,7 +119,7 @@ class SGameTaskObjective : public ISerializable auto GetType_script() const { return m_task_type; } void SetType_script(int t) { m_task_type = (ETaskType)t; } - auto GetIDX_script() const { return m_idx; } + auto GetID() const { return m_idx; } auto GetTitle_script() const { return m_Title.c_str(); } void SetTitle_script(pcstr title) { m_Title = title; } @@ -121,8 +127,13 @@ class SGameTaskObjective : public ISerializable auto GetDescription_script() const { return m_Description.c_str(); } void SetDescription_script(pcstr desc) { m_Description = desc; } + // encyclopedia + void SetArticleID_script(cpcstr id) { m_article_id = id; } + void SetArticleKey_script(cpcstr key) { m_article_key = key; } + + // icon auto GetIconName_script() const { return m_icon_texture_name.c_str(); } - void SetIconName_script(pcstr tex) { m_icon_texture_name = tex; } + void SetIconName_script(pcstr tex); // map void SetMapHint_script(pcstr hint) { m_map_hint = hint; } @@ -145,28 +156,56 @@ class SGameTaskObjective : public ISerializable void CommitScriptHelperContents(); }; -class CGameTask : public SGameTaskObjective, public Noncopyable +using OBJECTIVES_VECTOR = xr_vector; + +class CGameTask final : public SGameTaskObjective, public Noncopyable { public: TASK_ID m_ID; u32 m_priority{}; bool m_read{}; +private: + OBJECTIVES_VECTOR m_Objectives; + TASK_OBJECTIVE_ID m_active_objective{ NO_TASK_OBJECTIVE }; + public: CGameTask(); + void Load(const TASK_ID& id); + void save(IWriter& stream) override; void load(IReader& stream) override; void ChangeStateCallback() override; + TASK_OBJECTIVE_ID ActiveObjectiveIdx() const; + SGameTaskObjective* ActiveObjective(); + SGameTaskObjective& Objective(TASK_OBJECTIVE_ID idx); + const SGameTaskObjective& Objective(TASK_OBJECTIVE_ID idx) const; + ETaskState ObjectiveState(TASK_OBJECTIVE_ID idx) const; + void SetActiveObjective(TASK_OBJECTIVE_ID idx); + TASK_OBJECTIVE_ID GetObjectivesCount() const; + + using SGameTaskObjective::SetTaskState; + void SetTaskState(ETaskState state, TASK_OBJECTIVE_ID objective_id); + bool HasObjectiveInProgress() const; + // map void OnArrived(); + CMapLocation* LinkedMapLocation() override; + + void FillEncyclopedia() const; // for scripting access + void Load_script(pcstr id) { Load(id); } + auto GetID_script() const { return m_ID.c_str(); } void SetID_script(pcstr id) { m_ID = id; } auto GetPriority_script() const { return m_priority; } void SetPriority_script(int prio) { m_priority = prio; } + + void AddObjective_script(SGameTaskObjective* O); + SGameTaskObjective* GetObjective_script(TASK_OBJECTIVE_ID objective_id); }; diff --git a/src/xrGame/GameTaskDefs.h b/src/xrGame/GameTaskDefs.h index 2c0d258229e..b85593e8b2e 100644 --- a/src/xrGame/GameTaskDefs.h +++ b/src/xrGame/GameTaskDefs.h @@ -21,10 +21,16 @@ enum ETaskType }; using TASK_ID = shared_str; +using TASK_OBJECTIVE_ID = u16; + +using TASK_ID_VECTOR = xr_vector; + +constexpr auto NO_TASK_OBJECTIVE = static_cast(-1); +constexpr auto ROOT_TASK_OBJECTIVE = static_cast(0); // task itself extern TASK_ID g_active_task_id[eTaskTypeCount]; -class CGameTask; +class CGameTask; struct SGameTaskKey : public ISerializable, public IPureDestroyableObject { TASK_ID task_id; diff --git a/src/xrGame/GameTask_script.cpp b/src/xrGame/GameTask_script.cpp index bc6c9000258..67a8b6dbf2f 100644 --- a/src/xrGame/GameTask_script.cpp +++ b/src/xrGame/GameTask_script.cpp @@ -3,6 +3,18 @@ #include "xrScriptEngine/ScriptExporter.hpp" using namespace luabind; +using namespace luabind::policy; + +void CGameTask::AddObjective_script(SGameTaskObjective* O) +{ + O->CommitScriptHelperContents(); + m_Objectives.emplace_back(*O); +} + +SGameTaskObjective* CGameTask::GetObjective_script(TASK_OBJECTIVE_ID objective_id) +{ + return &Objective(objective_id); +} SCRIPT_EXPORT(CGameTask, (), { @@ -27,12 +39,17 @@ SCRIPT_EXPORT(CGameTask, (), class_("SGameTaskObjective") .def(constructor()) + .def("get_idx", &SGameTaskObjective::GetID) + .def("get_title", &SGameTaskObjective::GetTitle_script) .def("set_title", &SGameTaskObjective::SetTitle_script) .def("get_description", &SGameTaskObjective::GetDescription_script) .def("set_description", &SGameTaskObjective::SetDescription_script) + .def("set_article_id", &CGameTask::SetArticleID_script) + .def("set_article_key", &CGameTask::SetArticleKey_script) + .def("get_icon_name", &SGameTaskObjective::GetIconName_script) .def("set_icon_name", &SGameTaskObjective::SetIconName_script) @@ -44,6 +61,8 @@ SCRIPT_EXPORT(CGameTask, (), .def("set_map_hint", &SGameTaskObjective::SetMapHint_script) .def("set_map_location", &SGameTaskObjective::SetMapLocation_script) .def("set_map_object_id", &SGameTaskObjective::SetMapObjectID_script) + .def("set_object_id", &SGameTaskObjective::SetMapObjectID_script) // Shadow of Chernobyl scripts + .def_readwrite("def_ml_enabled", &CGameTask::m_def_location_enabled) .def("remove_map_locations", &SGameTaskObjective::RemoveMapLocations) .def("change_map_location", &SGameTaskObjective::ChangeMapLocation) @@ -62,10 +81,17 @@ SCRIPT_EXPORT(CGameTask, (), class_("CGameTask") .def(constructor<>()) + .def("load", &CGameTask::Load_script) + .def("get_id", &CGameTask::GetID_script) .def("set_id", &CGameTask::SetID_script) .def("get_priority", &CGameTask::GetPriority_script) .def("set_priority", &CGameTask::SetPriority_script) + + .def("add_objective", &CGameTask::AddObjective_script, adopt<2>()) + .def("get_objective", &CGameTask::GetObjective_script) + + .def("get_objectives_cnt", &CGameTask::GetObjectivesCount) ]; }); diff --git a/src/xrGame/GametaskManager.cpp b/src/xrGame/GametaskManager.cpp index 60bff86fe98..2158eaa1f92 100644 --- a/src/xrGame/GametaskManager.cpp +++ b/src/xrGame/GametaskManager.cpp @@ -85,6 +85,18 @@ CGameTask* CGameTaskManager::HasGameTask(const TASK_ID& id, bool only_inprocess) return 0; } +CGameTask* CGameTaskManager::GiveGameTaskToActor(const TASK_ID& id, + u32 timeToComplete, bool bCheckExisting /*= true*/, u32 timer_ttl /*= 0*/) +{ + if (bCheckExisting && HasGameTask(id, false)) + return nullptr; + //CGameTask* t = xr_new(id); + CGameTask* t = xr_new(); + t->Load(id); + + return GiveGameTaskToActor(t, timeToComplete, bCheckExisting, timer_ttl); +} + CGameTask* CGameTaskManager::GiveGameTaskToActor(CGameTask* t, u32 timeToComplete, bool bCheckExisting, u32 timer_ttl) { t->CommitScriptHelperContents(); @@ -131,7 +143,7 @@ CGameTask* CGameTaskManager::GiveGameTaskToActor(CGameTask* t, u32 timeToComplet return t; } -void CGameTaskManager::SetTaskState(CGameTask* task, ETaskState state) +void CGameTaskManager::SetTaskState(CGameTask* task, ETaskState state, TASK_OBJECTIVE_ID objective_id /*= NO_TASK_OBJECTIVE*/) { m_flags.set(eChanged, TRUE); @@ -139,27 +151,44 @@ void CGameTaskManager::SetTaskState(CGameTask* task, ETaskState state) if (m_flags.test(eMultipleTasks)) type = task->GetTaskType(); - task->SetTaskState(state); + task->SetTaskState(state, objective_id); - if (ActiveTask(type) == task) + if (objective_id == NO_TASK_OBJECTIVE) { - //SetActiveTask ("", t->GetTaskType()); - g_active_task_id[type] = ""; + if (ActiveTask(type) == task) + { + //SetActiveTask ("", t->GetTaskType()); + g_active_task_id[type] = ""; + } } + else + { + const bool activeObj = task->ActiveObjective()->GetID() == objective_id; + const bool isRoot = objective_id == 0; + if ((isRoot || !task->HasObjectiveInProgress()) && (ActiveTask() == task)) + { + g_active_task_id[type] = ""; + } + else if (!isRoot && activeObj && objective_id != (task->GetObjectivesCount() - 1)) + { // not last objective + task->SetActiveObjective(objective_id); + } + } if (CurrentGameUI()) CurrentGameUI()->UpdatePda(); } -void CGameTaskManager::SetTaskState(const TASK_ID& id, ETaskState state) +void CGameTaskManager::SetTaskState(const TASK_ID& id, ETaskState state, TASK_OBJECTIVE_ID objective_id /*= NO_TASK_OBJECTIVE*/) { - CGameTask* t = HasGameTask(id, true); + const bool objectiveSpecified = objective_id != NO_TASK_OBJECTIVE; + CGameTask* t = HasGameTask(id, objectiveSpecified); if (NULL == t) { - Msg("actor does not has task [%s] or it is completed", *id); + Msg("! actor does not has task [%s]%s", *id, objectiveSpecified ? "" : " or it is completed"); return; } - SetTaskState(t, state); + SetTaskState(t, state, objective_id); } void CGameTaskManager::UpdateTasks() @@ -178,18 +207,24 @@ void CGameTaskManager::UpdateTasks() Tasks tasks( xr_alloca(task_count * sizeof(SGameTaskKey)), task_count, GetGameTasks().begin(), GetGameTasks().end()); - Tasks::const_iterator I = tasks.begin(); - Tasks::const_iterator E = tasks.end(); - for (; I != E; ++I) + for (const SGameTaskKey& taskKey : tasks) { - CGameTask* const t = (*I).game_task; + CGameTask* const t = taskKey.game_task; if (t->GetTaskState() != eTaskStateInProgress) continue; - ETaskState const state = t->UpdateState(); + const auto objectives = t->GetObjectivesCount(); + for (TASK_OBJECTIVE_ID i = 0; i < objectives; ++i) + { + SGameTaskObjective& obj = t->Objective(i); + if (obj.GetTaskState() != eTaskStateInProgress) + continue; + + ETaskState const state = obj.UpdateState(); - if ((state == eTaskStateFail) || (state == eTaskStateCompleted)) - SetTaskState(t, state); + if ((state == eTaskStateFail) || (state == eTaskStateCompleted)) + SetTaskState(t, state, i); + } } } @@ -252,7 +287,7 @@ void CGameTaskManager::SetActiveTask(const TASK_ID& id, ETaskType type) m_read = true; }*/ -void CGameTaskManager::SetActiveTask(CGameTask* task) +void CGameTaskManager::SetActiveTask(CGameTask* task, TASK_OBJECTIVE_ID objective_id) { VERIFY(task); if (task) @@ -262,11 +297,21 @@ void CGameTaskManager::SetActiveTask(CGameTask* task) type = task->GetTaskType(); g_active_task_id[type] = task->m_ID; + task->SetActiveObjective(objective_id); + m_flags.set(eChanged, TRUE); task->m_read = true; } } +void CGameTaskManager::SetActiveTask(CGameTask* task) +{ + auto objective = task->ActiveObjectiveIdx(); + if (objective == NO_TASK_OBJECTIVE && task->GetObjectivesCount() > 1) + objective = 1; + SetActiveTask(task, objective); +} + CUIMapWnd* GetMapWnd(); void CGameTaskManager::MapLocationRelcase(CMapLocation* ml) diff --git a/src/xrGame/GametaskManager.h b/src/xrGame/GametaskManager.h index 99a13e45bb5..6622085e0a8 100644 --- a/src/xrGame/GametaskManager.h +++ b/src/xrGame/GametaskManager.h @@ -37,15 +37,17 @@ class CGameTaskManager vGameTasks& GetGameTasks(); CGameTask* HasGameTask(const CMapLocation* ml, bool only_inprocess); CGameTask* HasGameTask(const TASK_ID& id, bool only_inprocess); + CGameTask* GiveGameTaskToActor(const TASK_ID& id, u32 timeToComplete, bool bCheckExisting = true, u32 timer_ttl = 0); CGameTask* GiveGameTaskToActor(CGameTask* t, u32 timeToComplete, bool bCheckExisting, u32 timer_ttl); - void SetTaskState(const TASK_ID& id, ETaskState state); - void SetTaskState(CGameTask* t, ETaskState state); + void SetTaskState(const TASK_ID& id, ETaskState state, TASK_OBJECTIVE_ID objective_id = NO_TASK_OBJECTIVE); + void SetTaskState(CGameTask* t, ETaskState state, TASK_OBJECTIVE_ID objective_id = NO_TASK_OBJECTIVE); void UpdateTasks(); CGameTask* ActiveTask(ETaskType type = eTaskTypeStoryline); //void SetActiveTask(const TASK_ID& id, ETaskType type = eTaskTypeStoryline); void SetActiveTask(CGameTask* task); + void SetActiveTask(CGameTask* task, TASK_OBJECTIVE_ID objective_id); u32 ActualFrame() const { return m_actual_frame; } CGameTask* IterateGet(CGameTask* t, ETaskState state, ETaskType type, bool bForward); u32 GetTaskIndex(CGameTask* t, ETaskState state, ETaskType type = eTaskTypeStoryline); diff --git a/src/xrGame/InfoPortion.cpp b/src/xrGame/InfoPortion.cpp index b84e90864dd..9e377ff7f3f 100644 --- a/src/xrGame/InfoPortion.cpp +++ b/src/xrGame/InfoPortion.cpp @@ -95,6 +95,15 @@ void CInfoPortion::load_shared(LPCSTR) THROW(article_str_id); info_data()->m_ArticlesDisable.emplace_back(article_str_id); } + + info_data()->m_GameTasks.clear(); + const int task_num = pXML->GetNodesNum(pNode, "task"); + for (int i = 0; i < task_num; ++i) + { + cpcstr task_str_id = pXML->Read(pNode, "task", i, nullptr); + THROW(task_str_id); + info_data()->m_GameTasks.emplace_back(task_str_id); + } } void CInfoPortion::InitXmlIdToIndex() diff --git a/src/xrGame/InfoPortion.h b/src/xrGame/InfoPortion.h index d0ae2027edb..114e2439205 100644 --- a/src/xrGame/InfoPortion.h +++ b/src/xrGame/InfoPortion.h @@ -23,6 +23,9 @@ struct SInfoPortionData : CSharedResource //нужно заменить одну статью другой) ARTICLE_ID_VECTOR m_ArticlesDisable; + //присоединенные задания + TASK_ID_VECTOR m_GameTasks; + //скриптовые действия, которые активируется после того как //информацию получает персонаж CDialogScriptHelper m_InfoScriptHelper; @@ -52,6 +55,7 @@ class CInfoPortion : public CSharedClass, virtual void Load(shared_str info_str_id); const ARTICLE_ID_VECTOR& Articles() const { return info_data()->m_Articles; } const ARTICLE_ID_VECTOR& ArticlesDisable() const { return info_data()->m_ArticlesDisable; } + const TASK_ID_VECTOR& GameTasks() const { return info_data()->m_GameTasks; } const DIALOG_ID_VECTOR& DialogNames() const { return info_data()->m_DialogNames; } const SInfoPortionData::INFO_ID_VECTOR& DisableInfos() const { return info_data()->m_DisableInfo; } diff --git a/src/xrGame/actor_communication.cpp b/src/xrGame/actor_communication.cpp index 3dae89f1629..c23d578071d 100644 --- a/src/xrGame/actor_communication.cpp +++ b/src/xrGame/actor_communication.cpp @@ -94,6 +94,24 @@ void CActor::AddEncyclopediaArticle(const CInfoPortion* info_portion) const } +void CActor::AddGameTask(const CInfoPortion* info_portion) const +{ + VERIFY2(info_portion, "info_portion is nullptr"); + if (!info_portion) + { + Msg("! [%s] info_portion is nullptr!", __FUNCTION__); + return; + } + const TASK_ID_VECTOR& tasks = info_portion->GameTasks(); + if (tasks.empty()) + return; + + for (const TASK_ID& taskId : tasks) + { + Level().GameTaskManager().GiveGameTaskToActor(taskId, 0); + } +} + void CActor::AddGameNews(GAME_NEWS_DATA& news_data) { GAME_NEWS_VECTOR& news_vector = game_news_registry->registry().objects(); @@ -122,6 +140,7 @@ bool CActor::OnReceiveInfo(shared_str info_id) const info_portion.Load(info_id); AddEncyclopediaArticle(&info_portion); + AddGameTask(&info_portion); callback(GameObject::eInventoryInfo)(lua_game_object(), *info_id); diff --git a/src/xrGame/script_game_object.h b/src/xrGame/script_game_object.h index d75d1853a3c..5028adc8f50 100644 --- a/src/xrGame/script_game_object.h +++ b/src/xrGame/script_game_object.h @@ -15,6 +15,7 @@ #include "character_info_defs.h" #include "xrAICore/Navigation/game_graph_space.h" #include "game_location_selector.h" +#include "GameTaskDefs.h" // fwd. decl. namespace ALife @@ -336,8 +337,8 @@ class CScriptGameObject xrTime GetInfoTime(LPCSTR info_id); //работа с заданиями - ETaskState GetGameTaskState(LPCSTR task_id); - void SetGameTaskState(ETaskState state, LPCSTR task_id); + ETaskState GetGameTaskState(pcstr task_id, TASK_OBJECTIVE_ID objective_id); + void SetGameTaskState(ETaskState state, pcstr task_id, TASK_OBJECTIVE_ID objective_id); void GiveTaskToActor(CGameTask* t, u32 dt, bool bCheckExisting, u32 t_timer); void SetActiveTask(CGameTask* t); bool IsActiveTask(CGameTask* t); diff --git a/src/xrGame/script_game_object_inventory_owner.cpp b/src/xrGame/script_game_object_inventory_owner.cpp index 4ad1dab8a5b..12540e0338e 100644 --- a/src/xrGame/script_game_object_inventory_owner.cpp +++ b/src/xrGame/script_game_object_inventory_owner.cpp @@ -796,21 +796,25 @@ LPCSTR CScriptGameObject::sound_voice_prefix() const } #include "GametaskManager.h" -ETaskState CScriptGameObject::GetGameTaskState(LPCSTR task_id) +ETaskState CScriptGameObject::GetGameTaskState(LPCSTR task_id, TASK_OBJECTIVE_ID objective_id) { shared_str shared_name = task_id; CGameTask* t = Level().GameTaskManager().HasGameTask(shared_name, true); if (NULL == t) return eTaskStateDummy; - - return t->GetTaskState(); + if (objective_id >= t->GetObjectivesCount()) + { + GEnv.ScriptEngine->script_log(LuaMessageType::Error, "wrong objective num", task_id); + return eTaskStateDummy; + } + return t->ObjectiveState(objective_id); } -void CScriptGameObject::SetGameTaskState(ETaskState state, LPCSTR task_id) +void CScriptGameObject::SetGameTaskState(ETaskState state, LPCSTR task_id, TASK_OBJECTIVE_ID objective_id) { shared_str shared_name = task_id; - Level().GameTaskManager().SetTaskState(shared_name, state); + Level().GameTaskManager().SetTaskState(shared_name, state, objective_id); } ////////////////////////////////////////////////////////////////////////// diff --git a/src/xrGame/script_game_object_script3.cpp b/src/xrGame/script_game_object_script3.cpp index c4ba1a428b7..de6d46fc31a 100644 --- a/src/xrGame/script_game_object_script3.cpp +++ b/src/xrGame/script_game_object_script3.cpp @@ -185,8 +185,20 @@ class_& script_register_game_object2(class_GetGameTaskState(task_id, NO_TASK_OBJECTIVE); + }) .def("set_task_state", &CScriptGameObject::SetGameTaskState) + .def("set_task_state", +[](CScriptGameObject* self, ETaskState state, pcstr task_id) + { + self->SetGameTaskState(state, task_id, NO_TASK_OBJECTIVE); + }) .def("give_task", &CScriptGameObject::GiveTaskToActor, adopt<2>()) + .def("give_task", +[](CScriptGameObject* self, CGameTask* t, u32 dt, bool bCheckExisting) + { + self->GiveTaskToActor(t, dt, bCheckExisting, 0); + }, adopt<2>()) .def("set_active_task", &CScriptGameObject::SetActiveTask) .def("is_active_task", &CScriptGameObject::IsActiveTask) .def("get_task", &CScriptGameObject::GetTask)