660 lines
22 KiB
Lua
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
|