local ____lualib = require("lualib_bundle") local __TS__Class = ____lualib.__TS__Class local Map = ____lualib.Map local __TS__New = ____lualib.__TS__New local Set = ____lualib.Set local __TS__Iterator = ____lualib.__TS__Iterator local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter local __TS__ArrayMap = ____lualib.__TS__ArrayMap local ____exports = {} local ____recipes = require("cooking.recipes") local isRecipeUnlockedForResult = ____recipes.isRecipeUnlockedForResult local RECIPES = ____recipes.RECIPES require("cooking.modifier_campfire_cooking") local ____QuestSystem = require("quests.QuestSystem") local QuestSystem = ____QuestSystem.QuestSystem ____exports.CookingSystem = __TS__Class() local CookingSystem = ____exports.CookingSystem CookingSystem.name = "CookingSystem" CookingSystem.____file_path = "scripts/vscripts/cooking/CookingSystem.lua" function CookingSystem.prototype.____constructor(self) self.campfires = __TS__New(Map) self.detectionRadius = 300 self.isChecking = false self.cookingDuration = 5 self.openDistance = 400 self.firstTimePlayers = __TS__New(Set) self:initialize() end function CookingSystem.getInstance(self) if not ____exports.CookingSystem.instance then ____exports.CookingSystem.instance = __TS__New(____exports.CookingSystem) end return ____exports.CookingSystem.instance end function CookingSystem.initialize(self) ____exports.CookingSystem:getInstance() end function CookingSystem.prototype.initialize(self) CustomGameEventManager:RegisterListener( "cooking_start", function(_, event) self:onCookingStart(event) end ) CustomGameEventManager:RegisterListener( "cooking_claim_dish", function(_, event) self:onClaimDish(event) end ) CustomGameEventManager:RegisterListener( "cooking_request_update", function(_, event) self:onRequestUpdate(event) end ) CustomGameEventManager:RegisterListener( "cooking_panel_close", function(_, event) self:onPanelClose(event) end ) ListenToGameEvent( "game_rules_state_change", function() self:onGameStateChange() end, nil ) ListenToGameEvent( "npc_spawned", function(event) self:onNpcSpawned(event) end, nil ) end function CookingSystem.prototype.onGameStateChange(self) local state = GameRules:State_Get() if state == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS and not self.isChecking then Timers:CreateTimer( 0.5, function() self:startChecking() return nil end ) end end function CookingSystem.prototype.onNpcSpawned(self, event) if not IsServer() then return end local unit = EntIndexToHScript(event.entindex) if not unit or not IsValidEntity(unit) then return end local unitName = unit:GetUnitName() if unitName == "npc_campfire" then local index = unit:GetEntityIndex() Timers:CreateTimer( 0.1, function() if IsValidEntity(unit) then self:registerCampfire(unit) end return nil end ) end end function CookingSystem.prototype.registerCampfire(self, campfire) local index = campfire:GetEntityIndex() if not campfire:HasModifier("modifier_campfire_cooking") then campfire:AddNewModifier( campfire, getModifierSourceAbility(nil, campfire), "modifier_campfire_cooking", {} ) end if not self.campfires:has(index) then self.campfires:set( index, { campfire = campfire, nearbyHeroes = __TS__New(Set) } ) end end function CookingSystem.prototype.startChecking(self) if self.isChecking then return end self.isChecking = true self:scanForExistingCampfires() local checkInterval = 0.5 local checkLoop checkLoop = function() if not self.isChecking then return end if self.campfires.size > 0 then self:checkNearbyCampfires() else self:scanForExistingCampfires() end Timers:CreateTimer(checkInterval, checkLoop) end checkLoop(nil) end function CookingSystem.prototype.scanForExistingCampfires(self) if not IsServer() then return end local foundCount = 0 local classnames = {"npc_dota_base_addon", "npc_dota_creature", "npc_dota_base"} for ____, classname in ipairs(classnames) do local allUnits = Entities:FindAllByClassname(classname) for ____, unit in ipairs(allUnits) do do if not unit or not IsValidEntity(unit) then goto __continue34 end local unitName = unit:GetUnitName() if unitName == "npc_campfire" then local index = unit:GetEntityIndex() if not self.campfires:has(index) then self:registerCampfire(unit) foundCount = foundCount + 1 end end end ::__continue34:: end end local currentEntity = Entities:First() while currentEntity ~= nil do if IsValidEntity(currentEntity) then local unit = currentEntity if unit.GetUnitName and unit:GetUnitName() == "npc_campfire" then local index = unit:GetEntityIndex() if not self.campfires:has(index) then self:registerCampfire(unit) foundCount = foundCount + 1 end end end currentEntity = Entities:Next(currentEntity) end end function CookingSystem.prototype.checkNearbyCampfires(self) if not IsServer() then return end if self.campfires.size == 0 then return end local allHeroes = {} do local playerId = 0 while playerId < PlayerResource:GetPlayerCount() do if PlayerResource:IsValidPlayerID(playerId) then local hero = PlayerResource:GetSelectedHeroEntity(playerId) if hero and IsValidEntity(hero) and hero:IsRealHero() then allHeroes[#allHeroes + 1] = hero end end playerId = playerId + 1 end end for ____, ____value in __TS__Iterator(self.campfires:entries()) do local index = ____value[1] local data = ____value[2] do local campfire = data.campfire if not campfire or not IsValidEntity(campfire) then self.campfires:delete(index) goto __continue50 end local campfirePos = campfire:GetAbsOrigin() if not campfirePos then goto __continue50 end local nearbyHeroes = __TS__New(Set) for ____, hero in ipairs(allHeroes) do do if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then goto __continue53 end local heroPos = hero:GetAbsOrigin() if not heroPos then goto __continue53 end local distance = ((campfirePos.x - heroPos.x) ^ 2 + (campfirePos.y - heroPos.y) ^ 2) ^ 0.5 local playerID = hero:GetPlayerOwnerID() if distance <= self.detectionRadius then nearbyHeroes:add(playerID) if data.lockedByPlayer == playerID and data.nearbyHeroes:has(playerID) then end end end ::__continue53:: end for ____, playerID in __TS__Iterator(data.nearbyHeroes) do if not nearbyHeroes:has(playerID) then if data.lockedByPlayer == playerID then data.lockedByPlayer = nil end local player = PlayerResource:GetPlayer(playerID) if player then CustomGameEventManager:Send_ServerToPlayer(player, "cooking_panel_remove", {campfireIndex = index}) end end end data.nearbyHeroes = nearbyHeroes end ::__continue50:: end end function CookingSystem.prototype.updateCookingPanel(self, campfireIndex, playerID) if not IsServer() then return end local data = self.campfires:get(campfireIndex) if not data then return end local campfire = data.campfire if not campfire or not IsValidEntity(campfire) then return end local player = PlayerResource:GetPlayer(playerID) if not player then return end local hero = player:GetAssignedHero() if not hero or not IsValidEntity(hero) then return end local modifier = campfire:FindModifierByName("modifier_campfire_cooking") if not modifier then campfire:AddNewModifier( campfire, getModifierSourceAbility(nil, campfire), "modifier_campfire_cooking", {} ) modifier = campfire:FindModifierByName("modifier_campfire_cooking") if not modifier then return end end local cookingOrders = modifier:GetCookingOrders() local readyDishes = modifier:GetReadyDishesForPlayer(playerID) local currentTime = GameRules:GetGameTime() local cookingOrdersData = __TS__ArrayMap( __TS__ArrayFilter( cookingOrders, function(____, order) return order.playerID == playerID end ), function(____, order) return { recipeIndex = order.recipeIndex, playerID = order.playerID, startTime = order.startTime, duration = order.duration, result = order.result, remainingTime = math.max(0, order.duration - (currentTime - order.startTime)) } end ) local readyDishesData = __TS__ArrayMap( readyDishes, function(____, dish) return {recipeIndex = dish.recipeIndex, playerID = dish.playerID, result = dish.result, charges = dish.charges} end ) local playerRecipes = {} do local i = 0 while i < #RECIPES do do local recipe = RECIPES[i + 1] if recipe.locked and not isRecipeUnlockedForResult(nil, recipe.result) then goto __continue76 end playerRecipes[#playerRecipes + 1] = { index = i, name = recipe.result, ingredients = recipe.ingredients, result = recipe.result, canCook = self:canCook(hero, recipe.ingredients), duration = recipe.duration ~= nil and recipe.duration or self.cookingDuration, category = recipe.category or "dish" } end ::__continue76:: i = i + 1 end end local panelData = {campfireIndex = campfireIndex, recipes = playerRecipes, cookingOrders = cookingOrdersData, readyDishes = readyDishesData} CustomGameEventManager:Send_ServerToPlayer(player, "cooking_panel_update", panelData) end function CookingSystem.prototype.onCookingStart(self, event) if not IsServer() then return end local playerID = event.PlayerID ~= nil and event.PlayerID or -1 if playerID == -1 then return end local player = PlayerResource:GetPlayer(playerID) if not player then return end local hero = player:GetAssignedHero() if not hero or not IsValidEntity(hero) then return end local recipeIndex = event.recipeIndex if recipeIndex < 0 or recipeIndex >= #RECIPES then return end local recipe = RECIPES[recipeIndex + 1] if recipe.locked and not isRecipeUnlockedForResult(nil, recipe.result) then CustomGameEventManager:Send_ServerToPlayer(player, "cooking_result", {success = false, error = "Рецепт закрыт"}) return end if not self:canCook(hero, recipe.ingredients) then CustomGameEventManager:Send_ServerToPlayer(player, "cooking_result", {success = false, error = "Недостаточно ингредиентов"}) return end local data = self.campfires:get(event.campfireIndex) if not data then return end local campfire = data.campfire if not campfire or not IsValidEntity(campfire) then return end local modifier = campfire:FindModifierByName("modifier_campfire_cooking") if not modifier then return end self:removeIngredients(hero, recipe.ingredients) local currentTime = GameRules:GetGameTime() local recipeDuration = recipe.duration ~= nil and recipe.duration or self.cookingDuration modifier:AddCookingOrder({ recipeIndex = recipeIndex, playerID = playerID, startTime = currentTime, duration = recipeDuration, result = recipe.result }) CustomGameEventManager:Send_ServerToPlayer(player, "cooking_result", {success = true, message = "Готовка начата"}) self:updateCookingPanel(event.campfireIndex, playerID) end function CookingSystem.prototype.onRequestUpdate(self, event) if not IsServer() then return end local playerID = event.PlayerID if playerID == nil then return end local player = PlayerResource:GetPlayer(playerID) if not player then return end local campfireIndex = event.campfireIndex self:updateCookingPanel(campfireIndex, playerID) end function CookingSystem.prototype.onPanelClose(self, event) if not IsServer() then return end local playerID = event.PlayerID if playerID == nil then return end local data = self.campfires:get(event.campfireIndex) if not data then return end if data.lockedByPlayer ~= playerID then return end data.lockedByPlayer = nil local player = PlayerResource:GetPlayer(playerID) if player then CustomGameEventManager:Send_ServerToPlayer(player, "cooking_panel_remove", {campfireIndex = event.campfireIndex}) end end function CookingSystem.prototype.onClaimDish(self, event) if not IsServer() then return end local ____temp_0 if event.playerID ~= nil then ____temp_0 = event.playerID else ____temp_0 = event.PlayerID ~= nil and event.PlayerID or -1 end local playerID = ____temp_0 if playerID == -1 then return end local player = PlayerResource:GetPlayer(playerID) if not player then return end local hero = player:GetAssignedHero() if not hero or not IsValidEntity(hero) then return end local data = self.campfires:get(event.campfireIndex) if not data then return end local campfire = data.campfire if not campfire or not IsValidEntity(campfire) then return end local modifier = campfire:FindModifierByName("modifier_campfire_cooking") if not modifier then return end local allReadyDishes = modifier:GetReadyDishes() local dish = nil local globalIndex = -1 if event.result ~= nil and event.recipeIndex ~= nil then do local i = 0 while i < #allReadyDishes do local d = allReadyDishes[i + 1] if d.result == event.result and d.recipeIndex == event.recipeIndex and d.playerID == playerID then dish = d globalIndex = i break end i = i + 1 end end elseif event.dishIndex ~= nil then local readyDishes = modifier:GetReadyDishesForPlayer(playerID) if event.dishIndex >= 0 and event.dishIndex < #readyDishes then dish = readyDishes[event.dishIndex + 1] do local i = 0 while i < #allReadyDishes do if allReadyDishes[i + 1] == dish then globalIndex = i break end i = i + 1 end end end end if not dish or globalIndex == -1 then return end local resultItem = hero:AddItemByName(dish.result) if not resultItem then local item = CreateItem(dish.result, nil, nil) if item then if dish.charges > 1 then item:SetCurrentCharges(dish.charges) end CreateItemOnPositionSync( hero:GetAbsOrigin(), item ) end else if dish.charges > 1 then resultItem:SetCurrentCharges(dish.charges) end end modifier:RemoveReadyDish(globalIndex) if dish.result == "item_grilled_meat" then local questSystem = QuestSystem:getInstance() local ____temp_1 if dish.charges > 0 then ____temp_1 = dish.charges else ____temp_1 = 1 end local portions = ____temp_1 questSystem:addQuestProgress("firestar_quest_gourmet", "cook_meat", portions) end self:updateCookingPanel(event.campfireIndex, playerID) end function CookingSystem.prototype.isCampfireOccupied(self, campfireIndex, playerID) if not IsServer() then return false end local data = self.campfires:get(campfireIndex) if not data then return false end return data.lockedByPlayer ~= nil and data.lockedByPlayer ~= playerID end function CookingSystem.prototype.onCampfireClick(self, campfireIndex, playerID) if not IsServer() then return end local data = self.campfires:get(campfireIndex) if not data then return end local campfire = data.campfire if not campfire or not IsValidEntity(campfire) then return end local player = PlayerResource:GetPlayer(playerID) if not player then return end if not self.firstTimePlayers:has(playerID) then self.firstTimePlayers:add(playerID) CustomGameEventManager:Send_ServerToPlayer(player, "cooking_intro_video", {videoName = "cm", campfireIndex = campfireIndex}) end if data.lockedByPlayer ~= nil and data.lockedByPlayer ~= playerID then return end if not player then return end local hero = player:GetAssignedHero() if not hero or not IsValidEntity(hero) then return end local campfirePos = campfire:GetAbsOrigin() local heroPos = hero:GetAbsOrigin() if not campfirePos or not heroPos then return end local distance = ((campfirePos.x - heroPos.x) ^ 2 + (campfirePos.y - heroPos.y) ^ 2) ^ 0.5 if distance <= self.openDistance then if data.lockedByPlayer == nil then data.lockedByPlayer = playerID end if not data.nearbyHeroes:has(playerID) then data.nearbyHeroes:add(playerID) end self:updateCookingPanel(campfireIndex, playerID) end end function CookingSystem.prototype.canCook(self, hero, ingredients) local ingredientCounts = __TS__New(Map) for ____, ingredient in ipairs(ingredients) do if ingredient and ingredient ~= "" then ingredientCounts:set( ingredient, (ingredientCounts:get(ingredient) or 0) + 1 ) end end for ____, ____value in __TS__Iterator(ingredientCounts:entries()) do local itemName = ____value[1] local requiredCount = ____value[2] local foundCount = 0 do local i = 0 while i < 15 do local item = hero:GetItemInSlot(i) if item and IsValidEntity(item) and item:GetAbilityName() == itemName then local charges = item:GetCurrentCharges() local initialCharges = item:GetInitialCharges() foundCount = foundCount + (charges > 0 and charges or (initialCharges > 0 and initialCharges or 1)) end i = i + 1 end end if foundCount < requiredCount then return false end end return true end function CookingSystem.prototype.removeIngredients(self, hero, ingredients) local ingredientCounts = __TS__New(Map) for ____, ingredient in ipairs(ingredients) do if ingredient and ingredient ~= "" then ingredientCounts:set( ingredient, (ingredientCounts:get(ingredient) or 0) + 1 ) end end for ____, ____value in __TS__Iterator(ingredientCounts:entries()) do local itemName = ____value[1] local countToRemove = ____value[2] local removedCount = 0 do local i = 0 while i < 15 and removedCount < countToRemove do local item = hero:GetItemInSlot(i) if item and IsValidEntity(item) and item:GetAbilityName() == itemName then local charges = item:GetCurrentCharges() local initialCharges = item:GetInitialCharges() local itemCharges = charges > 0 and charges or (initialCharges > 0 and initialCharges or 1) if itemCharges > 1 and removedCount + itemCharges <= countToRemove then hero:RemoveItem(item) removedCount = removedCount + itemCharges elseif itemCharges > 1 then local toRemove = countToRemove - removedCount item:SetCurrentCharges(itemCharges - toRemove) removedCount = removedCount + toRemove else hero:RemoveItem(item) removedCount = removedCount + 1 end end i = i + 1 end end end end return ____exports