Урок Универсальная система заданий

vulkantsk

Супермодератор
Команда форума
21 Июн 2017
1,137
196
www.dotabuff.com
Проект
Roshan defense
Сегодня я вам расскажу, как можно сделать простые задания :)С
С помошью данного гайда вы сможете сделать квесты типа:
  • Принеси мне палку = дам тебе толкалку
  • две кларетки принеси = и мангусик получи
  • каждый принесенный меч = улучшаю в убермеч
  • в общем можно придумать много чего интересного, зависит только от вашей фантазии
Параметры которые можно настроить в задании:
  • необходимый предмет для выполнения задания (обязательно)
  • необходимое количество предметов(стаков) для выполнения задания от 0 до бесконечности (обязательно)
  • количество опыта, даваемое каждому игроку за выполнение задания (опционально)
  • количество золота, даваемое каждому игроку за выполнение задания (опционально)
  • предмет, даваемый игроку за выполнение задания (опционально)
  • нужен ли значок над головой у юнита, дающего задания (опционально), 1-да, 0-нет
  • название квеста, который следующим после выполнения(опционально)
  • данное задание можно выполнять несколько раз. Взаимоисключает предыдущий пункт (опционально), 1-да, 0-нет

test.jpg
test1.jpg
Я приведу в пример два варианта настройки квеста
  1. в луа файле
  2. в дд файле

Первое задание:
  • необходимый предмет = "clarity potion"
  • необходимое количество = 2
  • награда опыта = 1500
  • награда золота = 750
  • награда предмет = "enchanted mango"
  • значок над головой = 1(да)
  • название следующего квеста = "quest_test2"
  • данное задание можно выполнять несколько раз = 0 (нет)
Второе задание(даётся после выполнения первого):
  • необходимый предмет = "belt of strength"
  • необходимое количество = 1
  • награда опыта = 1500
  • награда золота = 750
  • награда предмет = "ogre axe"
  • значок над головой = 0(нет)
  • название следующего квеста = nil(нет)
  • данное задание можно выполнять несколько раз = 1 (да)
Код:
"npc_dota_test_1"
{
        // General
        //
        "BaseClass"                "npc_dota_creature"    // Class of entity of link to.
        "Model"                        "models/heroes/chaos_knight/chaos_knight.vmdl"    // Model.
        "ModelScale"                "0.75"                                // old 1.0
        "Level"                        "5"
     
    // Abilities
        //----------------------------------------------------------------
     
        "Ability1"                    "quest_test1"

        // Movement
        //----------------------------------------------------------------
        "MovementCapabilities"        "DOTA_UNIT_CAP_MOVE_GROUND"            // Type of locomotion - ground, air
        "MovementSpeed"                "325"        // Speed
        "MovementTurnRate"            "1"        // Turning rate.
     
        //Inventory
        "Creature"
        {

            "AttachWearables"        
            {
                "Wearable1"        {    "ItemDef"        "181"            } //Horse
                "Wearable2"        {    "ItemDef"        "183"            } //Shoulders
                "Wearable3"     {    "ItemDef"        "184"        } //Helmet
                "Wearable4"        {    "ItemDef"        "185"        } //Weapon
                "Wearable5"        {    "ItemDef"        "186"        } //Shield
            }        
        }
}

"npc_dota_test_2"
{
        // General
        //
        "BaseClass"                "npc_dota_creature"    // Class of entity of link to.
        "Model"                        "models/heroes/chaos_knight/chaos_knight.vmdl"    // Model.
        "ModelScale"                "0.75"                                // old 1.0
        "Level"                        "5"
     
    // Abilities
        //----------------------------------------------------------------
     
        "Ability1"                    "quest_test01"

        // Movement
        //----------------------------------------------------------------
        "MovementCapabilities"        "DOTA_UNIT_CAP_MOVE_GROUND"            // Type of locomotion - ground, air
        "MovementSpeed"                "325"        // Speed
        "MovementTurnRate"            "1"        // Turning rate.
     
        //Inventory
        "Creature"
        {

            "AttachWearables"        
            {
                "Wearable1"        {    "ItemDef"        "181"            } //Horse
                "Wearable2"        {    "ItemDef"        "183"            } //Shoulders
                "Wearable3"     {    "ItemDef"        "184"        } //Helmet
                "Wearable4"        {    "ItemDef"        "185"        } //Weapon
                "Wearable5"        {    "ItemDef"        "186"        } //Shield
            }        
        }
}
Код:
    "quest_test1"
    {
        // General
        //-------------------------------------------------------------------------------------------------------------
        "BaseClass"                        "ability_lua"                                    
        "ScriptFile"                    "abilities/quest/quest_template"                                            
        "AbilityBehavior"                "DOTA_ABILITY_BEHAVIOR_PASSIVE"
        "AbilityTextureName"            "jakiro_liquid_fire"

        // Special
        //-------------------------------------------------------------------------------------------------------------
        "AbilitySpecial"
        {
            "01"
            {
                "var_type"                    "FIELD_INTEGER"
                "value_required"            "2"
            }
            "02"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_exp"                "1500"
            }
            "03"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_gold"                "750"
            }
        }
    }
    "quest_test2"
    {
        // General
        //-------------------------------------------------------------------------------------------------------------
        "BaseClass"                        "ability_lua"                                    
        "ScriptFile"                    "abilities/quest/quest_template"                                            
        "AbilityBehavior"                "DOTA_ABILITY_BEHAVIOR_PASSIVE"
        "AbilityTextureName"            "jakiro_liquid_fire"

        // Special
        //-------------------------------------------------------------------------------------------------------------
        "AbilitySpecial"
        {
            "01"
            {
                "var_type"                    "FIELD_INTEGER"
                "value_required"            "1"
            }
            "02"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_exp"                "1500"
            }
            "03"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_gold"                "750"
            }
        }
    }
    "quest_test01"
    {
        // General
        //-------------------------------------------------------------------------------------------------------------
        "BaseClass"                        "ability_datadriven"
        "AbilityBehavior"                "DOTA_ABILITY_BEHAVIOR_PASSIVE"
        "AbilityTextureName"            "jakiro_liquid_fire"

        // Special
        //-------------------------------------------------------------------------------------------------------------
        "AbilitySpecial"
        {
            "01"
            {
                "var_type"                    "FIELD_INTEGER"
                "value_required"            "2"
            }
            "02"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_exp"                "1500"
            }
            "03"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_gold"                "750"
            }
        }

        "OnCreated"
        {
            "RunScript"
            {
                "ScriptFile"    "abilities/quest/quest_template"
                "Function"        "OnQuestCreated"
                "quest_item"    "item_clarity"
                "reward_item"    "item_enchanted_mango"
                "particle"        "1"
                "next_quest"    "quest_test02"
                "reusable"        "0"
            }
        }    
     
    }
    "quest_test02"
    {
        // General
        //-------------------------------------------------------------------------------------------------------------
        "BaseClass"                        "ability_datadriven"
        "AbilityBehavior"                "DOTA_ABILITY_BEHAVIOR_PASSIVE"
        "AbilityTextureName"            "jakiro_liquid_fire"

        // Special
        //-------------------------------------------------------------------------------------------------------------
        "AbilitySpecial"
        {
            "01"
            {
                "var_type"                    "FIELD_INTEGER"
                "value_required"            "1"
            }
            "02"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_exp"                "1500"
            }
            "03"
            {
                "var_type"                    "FIELD_INTEGER"
                "reward_gold"                "750"
            }
        }

        "OnCreated"
        {
            "RunScript"
            {
                "ScriptFile"    "abilities/quest/quest_template"
                "Function"        "OnQuestCreated"
                "quest_item"    "item_belt_of_strength"
                "reward_item"    "item_ogre_axe"
                "particle"        "0"
                //"next_quest"    "quest_test02"
                "reusable"        "1"
            }
        }
    }
Код:
LinkLuaModifier("modifier_quest_template", "abilities/quest/quest_template", LUA_MODIFIER_MOTION_NONE)

quest_template = class({})

function quest_template:GetIntrinsicModifierName()
    return "modifier_quest_template"
end
--------------------------------------------------------
------------------------------------------------------------

modifier_quest_template = class({
    IsHidden                 = function(self) return false end,
    IsPurgable                 = function(self) return false end,
    IsDebuff                 = function(self) return false end,
    IsBuff                  = function(self) return true end,
    RemoveOnDeath             = function(self) return true end,
    GetAttributes             = function(self) return MODIFIER_ATTRIBUTE_MULTIPLE end,
})

function modifier_quest_template:OnCreated()
    if IsServer() then
        local ability = self:GetAbility()
        self.value_required    = ability:GetSpecialValueFor("value_required")
        self.reward_exp     = ability:GetSpecialValueFor("reward_exp")
        self.reward_gold     = ability:GetSpecialValueFor("reward_gold")
--        self.value_required    = ability.value_required
--        self.reward_exp     = ability.reward_exp
--        self.reward_gold     = ability.reward_gold
        self.quest_item     = ability.quest_item
        self.reward_item     = ability.reward_item
        self.particle          = ability.particle
        self.current_quest  = ability:GetName()
        self.next_quest      = ability.next_quest
        self.reusable          = ability.reusable

        if self.particle == 1 then
            local parent = self:GetParent()
            local effect = "particles/generic_gameplay/generic_has_quest.vpcf"
            local pfx = ParticleManager:CreateParticle(effect, PATTACH_OVERHEAD_FOLLOW, parent)
            self:AddParticle(pfx, true, false, 111, false, false)

        end
        print(self.value_required)
        print(self.reward_exp)
        print(self.reward_gold)
        print(self.quest_item)
        print(self.reward_item)
        print(self.particle)
        print(self.current_quest)
        print(self.next_quest)
        print(self.reusable)

        self:StartIntervalThink(0.1)    
    end
end

function modifier_quest_template:OnIntervalThink()
    local parent = self:GetParent()
 
    local heroes = FindUnitsInRadius(parent:GetTeamNumber(),
                                    parent:GetAbsOrigin(),
                                    nil,
                                    350,
                                    DOTA_UNIT_TARGET_TEAM_BOTH,
                                    DOTA_UNIT_TARGET_HERO,
                                    DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES + DOTA_UNIT_TARGET_FLAG_NO_INVIS,
                                    FIND_CLOSEST,
                                    false)
    for i = 1, #heroes do
        local hero = heroes[1]
        for j = 0,9 do
            local item = hero:GetItemInSlot(j)
            if item and item:GetName() == self.quest_item then
                local item_charges = item:GetCurrentCharges()
                if self.value_required < 2 or item_charges >= self.value_required then
                    hero:RemoveItem( item )
                    if self.reward_exp > 0 then
                        hero:GiveExperiencePlayers( self.reward_exp )
                    end
                    if self.reward_gold > 0 then
                        hero:GiveGoldPlayers( self.reward_gold )
                    end
                    EmitGlobalSound("ui.inv_pickup_highvalue")
                 
                    if self.reward_item then
                        parent:DropQuestItem( hero, self.reward_item )
                    end
                    if self.reusable == 0 then
                        if self.current_quest then
                            parent:RemoveAbility(self.current_quest)
                            self:Destroy()
                        end
                        if self.next_quest then
                            parent:AddAbility(self.next_quest):SetLevel(1)
                        end
                    end
                end
            end
        end
    end
end

quest_test1 = class(quest_template)

function quest_test1:Spawn()
    self.quest_item     = "item_clarity"    -- название предмета, который нужен для завершения задания
    self.reward_item     = "item_enchanted_mango" -- название предмета, который получен за прохождение задания(опционально)
    self.particle         = 1    -- нужен ли эффект над головой ? 1 - да, 0 - нет
    self.next_quest     = "quest_test2" -- название следующего квеста (опционально)
    self.reusable         = 0 -- данный квест можно выполнять много кратно. 0 - нет, 1 - да
end

quest_test2 = class(quest_template)

function quest_test2:Spawn()
    self.quest_item     = "item_belt_of_strength"    -- название предмета, который нужен для завершения задания
    self.reward_item     = "item_ogre_axe" -- название предмета, который получен за прохождение задания(опционально)
    self.particle         = 0    -- нужен ли эффект над головой ? 1 - да, 0 - нет
    self.next_quest     = nil -- название следующего квеста (опционально)
    self.reusable         = 1 -- данный квест можно выполнять много кратно. 0 - нет, 1 - да
end
---------------------------------------------------------------
---------------------------------------------------------------

function OnQuestCreated(data)
    Timers:CreateTimer(0, function()
        local ability = data.ability
        local caster = data.caster

        ability.quest_item         = data.quest_item or nil
        ability.reward_item     = data.reward_item or nil
        ability.particle         = data.particle or nil
        ability.next_quest         = data.next_quest or nil
        ability.reusable         = data.reusable or nil

        caster:AddNewModifier(caster, ability, "modifier_quest_template", nil)
    end)
end
---------------------------------------------------------------
---------------------------------------------------------------
if IsServer() then
    function CDOTA_BaseNPC:DropQuestItem( target, item_name )
        local unit = self            
        local spawnPoint = unit:GetAbsOrigin()    
        local newItem = CreateItem( item_name, nil, nil )
        local drop = CreateItemOnPositionForLaunch( spawnPoint, newItem )
        local initialPoint = target:GetAbsOrigin() + RandomVector( RandomFloat( 50, 125 ) )

        newItem:LaunchLootInitialHeight( false, 0, 150, 0.75, initialPoint )
    end

    function CDOTA_BaseNPC:GiveGoldPlayers( gold )
        local team = self:GetTeam()
        for index=0 ,9 do
            if PlayerResource:HasSelectedHero(index) then
                local player = PlayerResource:GetPlayer(index)
                local hero = PlayerResource:GetSelectedHeroEntity(index)
                local hero_team = hero:GetTeam()
                if hero_team == team then
                    hero:ModifyGold(gold, false, 0)
                    SendOverheadEventMessage( player, OVERHEAD_ALERT_GOLD, hero, gold, nil )
                end
            end
        end
    end

    function CDOTA_BaseNPC:GiveExperiencePlayers( experience )
        local team = self:GetTeam()
        for index=0 ,9 do
            if PlayerResource:HasSelectedHero(index) then
                local player = PlayerResource:GetPlayer(index)
                local hero = PlayerResource:GetSelectedHeroEntity(index)
                local hero_team = hero:GetTeam()
                if hero_team == team then
                    hero:AddExperience(experience, 0, false, true )
                end
            end
        end
    end
end

--------------------------------------------------------
------------------------------------------------------------

modifier_quest_template = class({
    IsHidden                 = function(self) return false end,
    IsPurgable                 = function(self) return false end,
    IsDebuff                 = function(self) return false end,
    IsBuff                  = function(self) return true end,
    RemoveOnDeath             = function(self) return true end,
    GetAttributes             = function(self) return MODIFIER_ATTRIBUTE_MULTIPLE end,
})

function modifier_quest_template:OnCreated()
    if IsServer() then
        local ability = self:GetAbility()
        self.value_required    = ability:GetSpecialValueFor("value_required")
        self.reward_exp     = ability:GetSpecialValueFor("reward_exp")
        self.reward_gold     = ability:GetSpecialValueFor("reward_gold")
--        self.value_required    = ability.value_required
--        self.reward_exp     = ability.reward_exp
--        self.reward_gold     = ability.reward_gold
        self.quest_item     = ability.quest_item
        self.reward_item     = ability.reward_item
        self.particle          = ability.particle
        self.current_quest  = ability:GetName()
        self.next_quest      = ability.next_quest
        self.reusable          = ability.reusable

        if self.particle == 1 then
            local parent = self:GetParent()
            local effect = "particles/generic_gameplay/generic_has_quest.vpcf"
            local pfx = ParticleManager:CreateParticle(effect, PATTACH_OVERHEAD_FOLLOW, parent)
            self:AddParticle(pfx, true, false, 111, false, false)

        end
        print(self.value_required)
        print(self.reward_exp)
        print(self.reward_gold)
        print(self.quest_item)
        print(self.reward_item)
        print(self.particle)
        print(self.current_quest)
        print(self.next_quest)
        print(self.reusable)

        self:StartIntervalThink(0.1)    
    end
end

function modifier_quest_template:OnIntervalThink()
    local parent = self:GetParent()
 
    local heroes = FindUnitsInRadius(parent:GetTeamNumber(),
                                    parent:GetAbsOrigin(),
                                    nil,
                                    350,
                                    DOTA_UNIT_TARGET_TEAM_BOTH,
                                    DOTA_UNIT_TARGET_HERO,
                                    DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES + DOTA_UNIT_TARGET_FLAG_NO_INVIS,
                                    FIND_CLOSEST,
                                    false)
    for i = 1, #heroes do
        local hero = heroes[1]
        for j = 0,9 do
            local item = hero:GetItemInSlot(j)
            if item and item:GetName() == self.quest_item then
                local item_charges = item:GetCurrentCharges()
                if self.value_required < 2 or item_charges >= self.value_required then
                    hero:RemoveItem( item )
                    if self.reward_exp > 0 then
                        hero:GiveExperiencePlayers( self.reward_exp )
                    end
                    if self.reward_gold > 0 then
                        hero:GiveGoldPlayers( self.reward_gold )
                    end
--                    EmitGlobalSound("ui.inv_pickup_highvalue")
                 
                    if self.reward_item then
                        parent:DropQuestItem( hero, self.reward_item )
                    end
                    if self.reusable == 0 then
                        if self.current_quest then
                            parent:RemoveAbility(self.current_quest)
                            self:Destroy()
                        end
                        if self.next_quest then
                            parent:AddAbility(self.next_quest):SetLevel(1)
                        end
                    end
                end
            end
        end
    end
end

quest_test1 = class(quest_template)

function quest_test1:Spawn()
    self.quest_item     = "item_clarity"    -- название предмета, который нужен для завершения задания
    self.reward_item     = "item_enchanted_mango" -- название предмета, который получен за прохождение задания(опционально)
    self.particle         = 1    -- нужен ли эффект над головой ? 1 - да, 0 - нет
    self.next_quest     = "quest_test2" -- название следующего квеста (опционально)
    self.reusable         = 0 -- данный квест можно выполнять много кратно. 0 - нет, 1 - да
end

quest_test2 = class(quest_template)

function quest_test2:Spawn()
    self.quest_item     = "item_belt_of_strength"    -- название предмета, который нужен для завершения задания
    self.reward_item     = "item_ogre_axe" -- название предмета, который получен за прохождение задания(опционально)
    self.particle         = 0    -- нужен ли эффект над головой ? 1 - да, 0 - нет
    self.next_quest     = nil -- название следующего квеста (опционально)
    self.reusable         = 1 -- данный квест можно выполнять много кратно. 0 - нет, 1 - да
end
---------------------------------------------------------------
---------------------------------------------------------------

function OnQuestCreated(data)
    Timers:CreateTimer(0, function()
        local ability = data.ability
        local caster = data.caster

        ability.quest_item         = data.quest_item or nil
        ability.reward_item     = data.reward_item or nil
        ability.particle         = data.particle or nil
        ability.next_quest         = data.next_quest or nil
        ability.reusable         = data.reusable or nil

        caster:AddNewModifier(caster, ability, "modifier_quest_template", nil)
    end)
end

Надеюсь сегодня вы научились, чему-то новому :)
Если есть какие-то идеи по улучшению гайда, то я готов выслушать !
 
Последнее редактирование:
  • Нравится
Реакции: UFO, ExcaliburS и kain

Дикий Пёс

Друзья CG
28 Июн 2017
411
96
Проект
Petri Reborn
думаю код можно сократить раз так в восемь и вообще не использовать абилки модифаеры и прочие костыли, но ты не ты без дд
 

vulkantsk

Супермодератор
Команда форума
21 Июн 2017
1,137
196
www.dotabuff.com
Проект
Roshan defense

xDes

Продвинутый
8 Ноя 2018
232
34
Проект
Boss Survival Adventure
Как по мне много лишней писанины, могу вечером, если надо, скинуть свой вариант миниквеста, он выбирает рандомно 1 из 5 предметов который надо принести чтобы из игры в игру не повторяться, и по выполнению дропает сундук с наградой, но можно любую награду выдать.
 

boss of this gym

Пользователь
18 Янв 2021
53
5
Как по мне много лишней писанины, могу вечером, если надо, скинуть свой вариант миниквеста, он выбирает рандомно 1 из 5 предметов который надо принести чтобы из игры в игру не повторяться, и по выполнению дропает сундук с наградой, но можно любую награду выдать.
Не мог бы ты мне скинуть?) Пригодилось бы)
 
Реклама: