Files
Dota-Zombie-Invasion/scripts/vscripts/cooking/cookingsystem.lua
T
2026-05-29 15:11:31 +07:00

660 lines
22 KiB
Lua

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