1733 lines
56 KiB
Lua
Executable File
1733 lines
56 KiB
Lua
Executable File
THTD_Custom_Hit_Block = {}
|
||
|
||
function GetHeroFairyList(hero)
|
||
local fairyList = {}
|
||
local count = 0
|
||
for k,v in pairs(hero.thtd_hero_tower_list) do
|
||
if v:GetUnitName() == "sunny" and v.thtd_combo_fairyList ~= nil then
|
||
table.insert(fairyList,v.thtd_combo_fairyList)
|
||
end
|
||
end
|
||
return fairyList
|
||
end
|
||
|
||
function IsUnitInFairy(fairyArea,unit)
|
||
local pos1 = fairyArea.sunny:GetAbsOrigin()
|
||
local pos2 = fairyArea.star:GetAbsOrigin()
|
||
local pos3 = fairyArea.luna:GetAbsOrigin()
|
||
|
||
local origin = unit:GetAbsOrigin()
|
||
|
||
if IsInTriangle(pos1,pos2,pos3,origin) == true then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|
||
|
||
function GetTriangleArea(p0,p1,p2)
|
||
local ab = Vector(p1.x - p0.x, p1.y - p0.y, 0)
|
||
local bc = Vector(p2.x - p1.x, p2.y - p1.y, 0)
|
||
return math.abs((ab.x * bc.y - ab.y * bc.x) / 2.0)
|
||
end
|
||
|
||
function IsInTriangle(a,b,c,d)
|
||
local sabc = GetTriangleArea(a, b, c)
|
||
local sadb = GetTriangleArea(a, d, b)
|
||
local sbdc = GetTriangleArea(b, d, c)
|
||
local sadc = GetTriangleArea(a, d, c)
|
||
|
||
local sumSuqar = sadb + sbdc + sadc
|
||
|
||
if (-5 < (sabc - sumSuqar) and (sabc - sumSuqar) < 5) then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|
||
|
||
--判断三点是否一条线
|
||
function IsThreePointsOnOneLine(p1,p2,p3)
|
||
local a = math.floor(GetDistanceBetweenTwoVec2D(p1, p2) + 0.5)
|
||
local b = math.floor(GetDistanceBetweenTwoVec2D(p2, p3) + 0.5)
|
||
local c = math.floor(GetDistanceBetweenTwoVec2D(p3, p1) + 0.5)
|
||
local max = math.max(a, b, c)
|
||
if a == max and a == (b + c) then return true end
|
||
if b == max and b == (a + c) then return true end
|
||
if c == max and c == (a + b) then return true end
|
||
return false
|
||
end
|
||
|
||
--求三角形的重心和最大半径
|
||
function GetCircleCenterAndRadius(p1,p2,p3)
|
||
local center = Vector((p1.x + p2.x + p3.x)/3, (p1.y + p2.y + p3.y)/3, 0)
|
||
return center, math.max(math.floor(GetDistanceBetweenTwoVec2D(p1, center) + 0.5), math.floor(GetDistanceBetweenTwoVec2D(p2, center) + 0.5), math.floor(GetDistanceBetweenTwoVec2D(p3, center) + 0.5))
|
||
end
|
||
|
||
-- 两点连线上任意点的坐标
|
||
function GetTwoVectorSub(pos1, pos2, p1vsp2)
|
||
--Px点到两端点的距离P1Px与PxP2比值为p1vsp2
|
||
local x=(pos1.x + p1vsp2 * pos2.x)/(1+p1vsp2)
|
||
local y=(pos1.y + p1vsp2 * pos2.y)/(1+p1vsp2)
|
||
local z=(pos1.z + p1vsp2 * pos2.z)/(1+p1vsp2)
|
||
return Vector(x, y, z)
|
||
end
|
||
|
||
--求三角形的外接圆圆心和半径,如果三点共线则返回nil
|
||
function GetOuterCircleCenterAndRadius(p1,p2,p3)
|
||
local a = math.floor(GetDistanceBetweenTwoVec2D(p1, p2) + 0.5)
|
||
local b = math.floor(GetDistanceBetweenTwoVec2D(p2, p3) + 0.5)
|
||
local c = math.floor(GetDistanceBetweenTwoVec2D(p3, p1) + 0.5)
|
||
|
||
--如果在一条线上,则返回中点
|
||
local max = math.max(a, b, c)
|
||
if a == max and a == (b + c) then
|
||
local x = (p1.x + p2.x) / 2
|
||
local y = (p1.y + p2.y) / 2
|
||
local r = a / 2
|
||
return Vector(math.floor(x + 0.5),math.floor(y + 0.5),0), r
|
||
end
|
||
if b == max and b == (a + c) then
|
||
local x = (p2.x + p3.x) / 2
|
||
local y = (p2.y + p3.y) / 2
|
||
local r = b / 2
|
||
return Vector(math.floor(x + 0.5),math.floor(y + 0.5),0), r
|
||
end
|
||
if c == max and c == (a + b) then
|
||
local x = (p1.x + p3.x) / 2
|
||
local y = (p1.y + p3.y) / 2
|
||
local r = c / 2
|
||
return Vector(math.floor(x + 0.5),math.floor(y + 0.5),0), r
|
||
end
|
||
|
||
--外接圆半径
|
||
local p = (a + b +c)/2
|
||
local s = math.sqrt(p*(p-a)*(p-b)*(p-c))
|
||
local r = math.floor(a*b*c/(4*s) + 0.5)
|
||
|
||
--外接圆圆心
|
||
local x1 = p1.x
|
||
local x2 = p2.x
|
||
local x3 = p3.x
|
||
local y1 = p1.y
|
||
local y2 = p2.y
|
||
local y3 = p3.y
|
||
local t1 = x1*x1+y1*y1
|
||
local t2 = x2*x2+y2*y2
|
||
local t3 = x3*x3+y3*y3
|
||
local temp = x1*y2+x2*y3+x3*y1-x1*y3-x2*y1-x3*y2
|
||
local x = (t2*y3+t1*y2+t3*y1-t2*y1-t3*y2-t1*y3)/temp/2
|
||
local y = (t3*x2+t2*x1+t1*x3-t1*x2-t2*x3-t3*x1)/temp/2
|
||
|
||
return Vector(math.floor(x + 0.5),math.floor(y + 0.5),0), r
|
||
end
|
||
|
||
--求三角形的内接圆圆心和半径
|
||
function GetInnerCircleCenterAndRadius(p0,p1,p2)
|
||
local x1 = p0.x
|
||
local x2 = p1.x
|
||
local x3 = p2.x
|
||
local y1 = p0.y
|
||
local y2 = p1.y
|
||
local y3 = p2.y
|
||
|
||
local x=((y2-y1)*(y3*y3-y1*y1+x3*x3-x1*x1)-(y3-y1)*(y2*y2-y1*y1+x2*x2-x1*x1))/(2*(x3-x1)*(y2-y1)-2*((x2-x1)*(y3-y1)))
|
||
local y=((x2-x1)*(x3*x3-x1*x1+y3*y3-y1*y1)-(x3-x1)*(x2*x2-x1*x1+y2*y2-y1*y1))/(2*(y3-y1)*(x2-x1)-2*((y2-y1)*(x3-x1)))
|
||
|
||
local radius = math.sqrt((p0.x - x)*(p0.x - x) + (p0.y - y)*(p0.y - y))
|
||
|
||
return Vector(x,y,0),radius
|
||
end
|
||
|
||
-- 获取各刷怪路线的偏移位置,x, y 为偏移量,增长方向默认为左右集中,一致向下
|
||
function GetSpawnLineOffsetVector(lineid,vec,x,y)
|
||
-- lineid从左上顺时针
|
||
if lineid == 2 or lineid == 3 then
|
||
return vec + Vector(-x,-y,0)
|
||
else
|
||
return vec + Vector(x,-y,0)
|
||
end
|
||
end
|
||
|
||
-- 判断点在直线哪一边,-1为AB的左侧,1为右侧
|
||
function PointToLineSide(point, vecA, vecB)
|
||
local a = vecB.y - vecA.y
|
||
local b = vecA.x - vecB.x
|
||
local c = vecB.x * vecA.y - vecA.x * vecB.y
|
||
local v = a * point.x + b * point.y + c
|
||
|
||
if v < 0 then
|
||
return -1
|
||
elseif v > 0 then
|
||
return 1
|
||
else
|
||
return 0
|
||
end
|
||
end
|
||
|
||
-- 是否在三角形内
|
||
function IsPointInTriangle(point, vecA, vecB, vecC)
|
||
local d1 = PointToLineSide(point, vecA, vecB)
|
||
local d2 = PointToLineSide(point, vecB, vecC)
|
||
local d3 = PointToLineSide(point, vecC, vecA)
|
||
return (d1 * d2 > 0) and (d2 * d3 > 0)
|
||
end
|
||
|
||
-- 点是否在直线AB左边,右手掌指向线段,大拇指方向
|
||
function IsLeftToLineSide(point, vecA, vecB)
|
||
return (vecB.x - vecA.x) * (point.y - vecA.y) > (point.x - vecA.x) * (vecB.y - vecA.y)
|
||
end
|
||
|
||
-- 点是否在三角形内
|
||
function IsPointInTriangle2(point, vecA, vecB, VecC)
|
||
local x = point.x
|
||
local y = point.y
|
||
local ax = vecA.x
|
||
local ay = vecA.y
|
||
local bx = vecB.x
|
||
local by = vecB.y
|
||
local cx = VecC.x
|
||
local cy = VecC.y
|
||
if (bx - ax) * (y - ay) > (by - ay) * (x - ax) and (cx - bx) * (y - by) > (cy - by) * (x - bx) and (ax - cx) * (y - cy) > (ay - cy) * (x - cx) then
|
||
return false
|
||
else
|
||
return true
|
||
end
|
||
end
|
||
|
||
-- 计算点与A、B两点形成的角度,即形成三角形中点所在顶点的角度
|
||
function GetAngle(point, vecA, vecB)
|
||
local a = (point - vecA):Length2D()
|
||
local b = (vecA - vecB):Length2D()
|
||
local c = (point - vecB):Length2D()
|
||
|
||
-- 计算弧度表示的角
|
||
local rad = math.acos((a*a + c*c - b*b)/(2.0*a*c));
|
||
-- 用角度表示的角
|
||
return 180*rad/math.pi
|
||
end
|
||
|
||
-- /**
|
||
-- * 点到线的最短距离实际上就是点到线的垂直距离。
|
||
-- * (x,y)为点的坐标
|
||
-- * (x1,y1)为线段上点的坐标
|
||
-- * (x2,y2)为线段另外一点的坐标
|
||
-- * 将点连接上两个线段的
|
||
-- */
|
||
function GetLengthPointToLine(point, vecA, vecB)
|
||
local x = point.x
|
||
local y = point.y
|
||
local x1 = vecA.x
|
||
local y1 = vecA.y
|
||
local x2 = vecB.x
|
||
local y2 = vecB.y
|
||
-- 三角形三个边长
|
||
local A = math.abs(math.sqrt(math.pow((x - x1), 2) + math.pow((y - y1), 2)))
|
||
local B = math.abs(math.sqrt(math.pow((x - x2), 2) + math.pow((y - y2), 2)))
|
||
local C = math.abs(math.sqrt(math.pow((x1 - x2), 2) + math.pow((y1 - y2), 2)))
|
||
-- 利用海伦公式计算三角形面积
|
||
-- 周长的一半
|
||
local P = (A + B + C) / 2
|
||
local allArea = math.abs(math.sqrt(P * (P - A) * (P - B) * (P - C)))
|
||
-- 普通公式计算三角形面积反推点到线的垂直距离
|
||
local dis = (2 * allArea) / C
|
||
return dis
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
-- 判断单位是否有效,即非空、存活、激活
|
||
function THTD_IsValid(unit)
|
||
if unit ~= nil and unit:IsNull() == false and unit:IsAlive() and unit:HasModifier("modifier_touhoutd_release_hidden") == false then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|
||
|
||
-- 判断是否有效且存活
|
||
function IsValidAlive(unit)
|
||
if unit ~= nil and unit:IsNull() == false and unit:IsAlive() then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
-- PlayerResource:ModifyGold 强化版,超出金钱将保存
|
||
thtd_extra_gold = {[0] = 0,[1] = 0,[2] = 0,[3] = 0}
|
||
function THTD_ModifyGoldEx(playerid, gold, reliable, flag)
|
||
if flag == nil then flag = DOTA_ModifyGold_Unspecified end
|
||
if reliable ~= false then reliable = true end
|
||
|
||
if thtd_extra_gold[playerid] == nil then
|
||
thtd_extra_gold[playerid] = 0
|
||
end
|
||
|
||
local current_gold = PlayerResource:GetGold(playerid)
|
||
if gold ~= nil then
|
||
local add_gold = gold
|
||
if current_gold + gold > 99999 then
|
||
add_gold = math.min(gold, 99999-current_gold)
|
||
thtd_extra_gold[playerid] = thtd_extra_gold[playerid] + (gold - add_gold)
|
||
elseif thtd_extra_gold[playerid] > 0 then
|
||
add_gold = math.min(thtd_extra_gold[playerid], 99999-(gold+current_gold))
|
||
thtd_extra_gold[playerid] = thtd_extra_gold[playerid] - add_gold
|
||
end
|
||
PlayerResource:ModifyGold(playerid, add_gold, reliable, flag)
|
||
else
|
||
if current_gold < 99999 and thtd_extra_gold[playerid] > 0 then
|
||
local move_gold = math.min(99999 - current_gold, thtd_extra_gold[playerid])
|
||
thtd_extra_gold[playerid] = thtd_extra_gold[playerid] - move_gold
|
||
PlayerResource:ModifyGold(playerid, move_gold, true, flag)
|
||
end
|
||
end
|
||
THTD_RefreshExtraGold(playerid)
|
||
end
|
||
|
||
-- 刷新超出上限金钱显示
|
||
function THTD_RefreshExtraGold(playerid)
|
||
PlayerResource:SetLastBuybackTime(playerid, thtd_extra_gold[playerid] or 0)
|
||
end
|
||
|
||
-- 获取总金钱
|
||
function CDOTA_BaseNPC:GetGold()
|
||
local playerid = self:GetPlayerOwnerID()
|
||
if PlayerResource:IsValidPlayerID(playerid) then
|
||
return PlayerResource:GetGold(playerid) + (thtd_extra_gold[playerid] or 0)
|
||
else
|
||
return 0
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
function THTD_IsTempleOfGodTower(unit)
|
||
local unitName = unit:GetUnitName()
|
||
if unitName == "miko" or unitName == "soga" or unitName == "futo" or unitName == "yoshika" or unitName == "seiga" then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|
||
|
||
function THTD_IsTempleOfGodCanBuffedTower(unit)
|
||
local unitName = unit:GetUnitName()
|
||
if unitName == "soga" or unitName == "futo" or unitName == "yoshika" or unitName == "seiga" then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|
||
|
||
function THTD_GetTempleOfGodBuffedTowerStarCount(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return 0 end
|
||
|
||
local star = 0
|
||
for k,v in pairs(hero.thtd_hero_tower_list) do
|
||
if THTD_IsValid(v) and THTD_IsTempleOfGodTower(v) then
|
||
if v:HasModifier("modifier_miko_02_buff") or v:GetUnitName() == "miko" then
|
||
star = star + v:THTD_GetStar()
|
||
end
|
||
end
|
||
end
|
||
return star
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
-- 是否星莲船单位
|
||
function THTD_IsStarLotusTower(unit)
|
||
local unitName = unit:GetUnitName()
|
||
if unitName == "byakuren" or unitName == "nazrin" or unitName == "toramaru" or unitName == "minamitsu" or unitName == "nue" or unitName == "kogasa" or unitName == "kyouko" then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|
||
|
||
-- 获取教徒数量,教徒不包括圣白莲
|
||
function THTD_GetStarLotusBuffedTowerCount(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return 0 end
|
||
|
||
local count = 0
|
||
for k,v in pairs(hero.thtd_hero_tower_list) do
|
||
if v:GetUnitName() ~= "byakuren" and THTD_IsStarLotusTower(v) then
|
||
if v:HasModifier("modifier_byakuren_03_buff") then
|
||
count = count + 1
|
||
end
|
||
end
|
||
end
|
||
return count
|
||
end
|
||
|
||
-- 获取星莲船单位数量
|
||
function THTD_GetStarLotusTowerCount(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return 0 end
|
||
|
||
local count = 0
|
||
for k,v in pairs(hero.thtd_hero_tower_list) do
|
||
if THTD_IsStarLotusTower(v) then
|
||
count = count + 1
|
||
end
|
||
end
|
||
return count
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
function THTD_FindFriendlyUnitsAll(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return {} end
|
||
|
||
return hero.thtd_hero_tower_list
|
||
end
|
||
|
||
function THTD_FindFriendlyUnitsInRadius(caster,point,radius)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return {} end
|
||
|
||
local targets = {}
|
||
for k,v in pairs(hero.thtd_hero_tower_list) do
|
||
if THTD_IsValid(v) then
|
||
if GetDistanceBetweenTwoVec2D(v:GetOrigin(), point) < radius then
|
||
table.insert(targets,v)
|
||
end
|
||
end
|
||
end
|
||
return targets
|
||
end
|
||
|
||
function THTD_FindUnitsInRadius(caster,point,radius)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return {} end
|
||
|
||
|
||
local id = hero.thtd_player_id
|
||
local targets = {}
|
||
if id ~= nil then
|
||
for k,v in pairs(THTD_EntitiesRectInner[id]) do
|
||
if THTD_IsValid(v) and v.thtd_is_outer ~= true then
|
||
if GetDistanceBetweenTwoVec2D(v:GetOrigin(), point) < radius then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
table.insert(targets,v)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if not(point.x+radius < 4432 and point.x-radius > -4432 and point.y+radius < 3896 and point.y-radius > -3896) then
|
||
for k,v in pairs(THTD_EntitiesRectOuter) do
|
||
if THTD_IsValid(v) then
|
||
if GetDistanceBetweenTwoVec2D(v:GetOrigin(), point) < radius then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
table.insert(targets,v)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
return targets
|
||
end
|
||
|
||
function THTD_FindUnitsAll(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return {} end
|
||
|
||
local id = hero.thtd_player_id
|
||
local targets = {}
|
||
if id ~= nil then
|
||
for k,v in pairs(THTD_EntitiesRectInner[id]) do
|
||
if THTD_IsValid(v) and v.thtd_is_outer ~= true then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
table.insert(targets,v)
|
||
end
|
||
end
|
||
end
|
||
|
||
for k,v in pairs(THTD_EntitiesRectOuter) do
|
||
if THTD_IsValid(v) then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
table.insert(targets,v)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
return targets
|
||
end
|
||
|
||
function THTD_FindUnitsInner(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then
|
||
print("------- THTD_FindUnitsInner : hero is nil, caster : "..caster:GetUnitName())
|
||
return {}
|
||
end
|
||
|
||
local id = hero.thtd_player_id
|
||
local targets = {}
|
||
if id ~= nil then
|
||
for k,v in pairs(THTD_EntitiesRectInner[id]) do
|
||
if THTD_IsValid(v) and v.thtd_is_outer ~= true then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
table.insert(targets,v)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
return targets
|
||
end
|
||
|
||
function THTD_FindUnitsOuter(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return {} end
|
||
|
||
local id = hero.thtd_player_id
|
||
local targets = {}
|
||
if id ~= nil then
|
||
for k,v in pairs(THTD_EntitiesRectOuter) do
|
||
if THTD_IsValid(v) then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
table.insert(targets,v)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
return targets
|
||
end
|
||
|
||
function THTD_HasUnitsInRadius(caster,point,radius)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return false end
|
||
|
||
local id = hero.thtd_player_id
|
||
if id ~= nil then
|
||
for k,v in pairs(THTD_EntitiesRectInner[id]) do
|
||
if THTD_IsValid(v) then
|
||
if GetDistanceBetweenTwoVec2D(v:GetOrigin(), point) < radius then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
return true
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if not(point.x+radius < 4432 and point.x-radius > -4432 and point.y+radius < 3896 and point.y-radius > -3896) then
|
||
for k,v in pairs(THTD_EntitiesRectOuter) do
|
||
if THTD_IsValid(v) then
|
||
if GetDistanceBetweenTwoVec2D(v:GetOrigin(), point) < radius then
|
||
if v.thtd_is_yukari_01_hidden ~= true then
|
||
return true
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
return false
|
||
end
|
||
|
||
function THTD_IsUnitInGroup(unit,group)
|
||
for k,v in pairs(group) do
|
||
if v == unit then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
-- _print = print
|
||
-- function print(str,value)
|
||
-- if PlayerResource ~= nil and tostring(PlayerResource:GetSteamID(0)) == string.decode("EE54BFF625C40D3D05AB039344290EA2A1","9527") then
|
||
-- _print(str, value)
|
||
-- end
|
||
-- end
|
||
-- _PrintTable = PrintTable
|
||
-- function PrintTable(t, indent, done)
|
||
-- if PlayerResource ~= nil and tostring(PlayerResource:GetSteamID(0)) == string.decode("EE54BFF625C40D3D05AB039344290EA2A1","9527") then
|
||
-- _PrintTable(t, indent, done)
|
||
-- end
|
||
-- end
|
||
-- _DeepPrintTable = DeepPrintTable
|
||
-- function DeepPrintTable(t)
|
||
-- if PlayerResource ~= nil and tostring(PlayerResource:GetSteamID(0)) == string.decode("EE54BFF625C40D3D05AB039344290EA2A1","9527") then
|
||
-- _DeepPrintTable(t)
|
||
-- end
|
||
-- end
|
||
|
||
|
||
function UnitStunTarget(caster,target,stuntime)
|
||
if target:HasModifier("modifier_bosses_random_resist_effect") then
|
||
target:AddNewModifier(caster, nil, "modifier_stunned", {duration=stuntime * 0.75})
|
||
else
|
||
target:AddNewModifier(caster, nil, "modifier_stunned", {duration=stuntime})
|
||
end
|
||
end
|
||
|
||
function UnitNoPathingfix(caster,target,duration)
|
||
target:AddNewModifier(caster, nil, "modifier_spectre_spectral_dagger_path_phased", {duration=duration})
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
function GetDistanceBetweenTwoVec2D(a, b)
|
||
local xx = (a.x-b.x)
|
||
local yy = (a.y-b.y)
|
||
return math.sqrt(xx*xx + yy*yy)
|
||
end
|
||
|
||
function GetDistanceBetweenTwoEntity(ent1,ent2)
|
||
local pos_1=ent1:GetOrigin()
|
||
local pos_2=ent2:GetOrigin()
|
||
local x_=(pos_1[1]-pos_2[1])^2
|
||
local y_=(pos_1[2]-pos_2[2])^2
|
||
local dis=(x_+y_)^(0.5)
|
||
return dis
|
||
end
|
||
|
||
function GetRadBetweenTwoVec2D(a,b)
|
||
local y = b.y - a.y
|
||
local x = b.x - a.x
|
||
return math.atan2(y,x)
|
||
end
|
||
|
||
function GetRadBetweenTwoVecZ3D(a,b)
|
||
local y = b.y - a.y
|
||
local x = b.x - a.x
|
||
local z = b.z - a.z
|
||
local s = math.sqrt(x*x + y*y)
|
||
return math.atan2(z,s)
|
||
end
|
||
|
||
--aVec:原点向量
|
||
--rectOrigin:单位原点向量
|
||
--rectWidth:矩形宽度
|
||
--rectLenth:矩形长度
|
||
--rectRad:矩形相对Y轴旋转角度
|
||
function IsRadInRect(aVec,rectOrigin,rectWidth,rectLenth,rectRad)
|
||
local aRad = GetRadBetweenTwoVec2D(rectOrigin,aVec)
|
||
local turnRad = aRad + (math.pi/2 - rectRad)
|
||
local aRadius = GetDistanceBetweenTwoVec2D(rectOrigin,aVec)
|
||
local turnX = aRadius*math.cos(turnRad)
|
||
local turnY = aRadius*math.sin(turnRad)
|
||
local maxX = rectWidth/2
|
||
local minX = -rectWidth/2
|
||
local maxY = rectLenth
|
||
local minY = 0
|
||
if(turnX<maxX and turnX>minX and turnY>minY and turnY<maxY)then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
return false
|
||
end
|
||
|
||
|
||
function IsValueInTable(Table,v)
|
||
if Table == nil then return false end
|
||
if type(Table) ~= "table" then return false end
|
||
for i= 1,#Table do
|
||
if v == Table[i] then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
-- cx = 目标的x
|
||
-- cy = 目标的y
|
||
-- ux = math.cos(theta) (rad是caster和target的夹角的弧度制表示)
|
||
-- uy = math.sin(theta)
|
||
-- r = 目标和原点之间的距离
|
||
-- theta = 夹角的弧度制
|
||
-- px = 原点的x
|
||
-- py = 原点的y
|
||
-- 返回 true or false(目标是否在扇形内,在的话=true,不在=false)
|
||
function IsPointInCircularSector(cx,cy,ux,uy,r,theta,px,py)
|
||
local dx = px - cx
|
||
local dy = py - cy
|
||
|
||
local length = math.sqrt(dx * dx + dy * dy)
|
||
|
||
if (length > r) then
|
||
return false
|
||
end
|
||
|
||
local vec = Vector(dx,dy,0):Normalized()
|
||
return math.acos(vec.x * ux + vec.y * uy) < theta
|
||
end
|
||
|
||
--删除table中的table,第二个参数为子表
|
||
function TableRemoveSubTable(table_1 , table_2)
|
||
for i,v in pairs(table_1) do
|
||
if v == table_2 then
|
||
table.remove(table_1,i)
|
||
return
|
||
end
|
||
end
|
||
end
|
||
|
||
GameRules.AbilityBehavior = {
|
||
DOTA_ABILITY_BEHAVIOR_ATTACK,
|
||
DOTA_ABILITY_BEHAVIOR_AURA,
|
||
DOTA_ABILITY_BEHAVIOR_AUTOCAST,
|
||
DOTA_ABILITY_BEHAVIOR_CHANNELLED,
|
||
DOTA_ABILITY_BEHAVIOR_DIRECTIONAL,
|
||
DOTA_ABILITY_BEHAVIOR_DONT_ALERT_TARGET,
|
||
DOTA_ABILITY_BEHAVIOR_DONT_CANCEL_MOVEMENT,
|
||
DOTA_ABILITY_BEHAVIOR_DONT_RESUME_ATTACK,
|
||
DOTA_ABILITY_BEHAVIOR_DONT_RESUME_MOVEMENT,
|
||
DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING,
|
||
DOTA_ABILITY_BEHAVIOR_IGNORE_CHANNEL,
|
||
DOTA_ABILITY_BEHAVIOR_IGNORE_PSEUDO_QUEUE,
|
||
DOTA_ABILITY_BEHAVIOR_IGNORE_TURN ,
|
||
DOTA_ABILITY_BEHAVIOR_IMMEDIATE,
|
||
DOTA_ABILITY_BEHAVIOR_ITEM,
|
||
DOTA_ABILITY_BEHAVIOR_NOASSIST,
|
||
DOTA_ABILITY_BEHAVIOR_NONE,
|
||
DOTA_ABILITY_BEHAVIOR_NORMAL_WHEN_STOLEN,
|
||
DOTA_ABILITY_BEHAVIOR_NOT_LEARNABLE,
|
||
DOTA_ABILITY_BEHAVIOR_ROOT_DISABLES,
|
||
DOTA_ABILITY_BEHAVIOR_RUNE_TARGET,
|
||
DOTA_ABILITY_BEHAVIOR_UNRESTRICTED ,
|
||
}
|
||
|
||
--判断单体技能
|
||
function CDOTABaseAbility:IsUnitTarget()
|
||
local b = self:GetBehavior()
|
||
|
||
if self:IsHidden() then b = b - 1 end
|
||
for k,v in pairs(GameRules.AbilityBehavior) do
|
||
repeat
|
||
if v == 0 then break end
|
||
b = b % v
|
||
until true
|
||
end
|
||
|
||
if (b - DOTA_ABILITY_BEHAVIOR_AOE) == DOTA_ABILITY_BEHAVIOR_UNIT_TARGET then
|
||
b = b - DOTA_ABILITY_BEHAVIOR_AOE
|
||
end
|
||
|
||
if b == DOTA_ABILITY_BEHAVIOR_UNIT_TARGET then
|
||
return true
|
||
end
|
||
return false
|
||
end
|
||
|
||
--判断点目标技能
|
||
function CDOTABaseAbility:IsPoint( )
|
||
local b = self:GetBehavior()
|
||
|
||
return bit.band(b,DOTA_ABILITY_BEHAVIOR_POINT) == DOTA_ABILITY_BEHAVIOR_POINT
|
||
end
|
||
|
||
--判断无目标技能
|
||
function CDOTABaseAbility:IsNoTarget( )
|
||
local b = self:GetBehavior()
|
||
|
||
return bit.band(b,DOTA_ABILITY_BEHAVIOR_NO_TARGET) == DOTA_ABILITY_BEHAVIOR_NO_TARGET
|
||
end
|
||
|
||
--通用方法之添加技能,传入单位(实体)、技能名(必传)、等级(默认1)
|
||
function AddAbilityAndSetLevel(u,a,l)
|
||
if l == nil then
|
||
l = 1
|
||
end
|
||
if u == nil or u:IsNull() then
|
||
return
|
||
end
|
||
if u:FindAbilityByName(a) == nil then
|
||
u:AddAbility(a)
|
||
if u:FindAbilityByName(a) ~= nil then
|
||
u:FindAbilityByName(a):SetLevel(l)
|
||
end
|
||
else
|
||
u:FindAbilityByName(a):SetLevel(l)
|
||
end
|
||
end
|
||
|
||
--增加buff,支持lua和技能的,如果存在则刷新,否则创建。sourceAbility为可选(lua的buff输入nil)
|
||
function CDOTA_BaseNPC:AddModifier(caster, sourceAbility, modifierName, duration, stackCount)
|
||
local modifier = self:FindModifierByName(modifierName)
|
||
if modifier ~= nil then
|
||
if duration ~= nil and duration > 0 then
|
||
modifier:SetDuration(duration, false)
|
||
end
|
||
else
|
||
if duration ~= nil and duration > 0 then
|
||
modifier = self:AddNewModifier(caster, sourceAbility, modifierName, {Duration = duration})
|
||
else
|
||
modifier = self:AddNewModifier(caster, sourceAbility, modifierName, nil)
|
||
end
|
||
end
|
||
if stackCount ~= nil and stackCount > 0 and modifier ~= nil then
|
||
modifier:SetStackCount(stackCount)
|
||
end
|
||
return modifier
|
||
end
|
||
|
||
-- 获取技能设定值,在技能的 AbilitySpecial 中定义,isConvertPercent 为是否将百分比的转为化对应小数
|
||
function CDOTA_BaseNPC:GetAbilityValue(abilityName, itemName, isConvertPercent)
|
||
local ability = self:FindAbilityByName(abilityName)
|
||
if ability == nil then
|
||
print("---------- GetAbilityValue Error!")
|
||
print(abilityName..": ability for "..self:GetUnitName().." is nil")
|
||
return 0
|
||
end
|
||
|
||
-- 如果没有定义,则 v 等于0不会等于nil
|
||
local v = ability:GetSpecialValueFor(itemName)
|
||
if isConvertPercent == true then
|
||
return v / 100
|
||
else
|
||
return v
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
--弹射函数
|
||
--用于检测是否被此次弹射命中过
|
||
function CatapultFindImpact( unit,str )
|
||
for i,v in pairs(unit.CatapultImpact) do
|
||
if v == str then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
--caster是施法者或者主要来源
|
||
--target是第一个目标
|
||
--ability是技能来源
|
||
--effectName是弹射的投射物
|
||
--move_speed是投射物的速率
|
||
--doge是表示能否被躲掉
|
||
--radius是每次弹射的范围
|
||
--count是弹射次数
|
||
--teams,types,flags获取单位的三剑客
|
||
--find_tpye是单位组按远近或者随机排列
|
||
-- FIND_CLOSEST
|
||
-- FIND_FARTHEST
|
||
-- FIND_UNITS_EVERYWHERE
|
||
function Catapult( caster,target,ability,effectName,move_speed,radius,count,teams,types,flags,find_tpye )
|
||
print("Run Catapult")
|
||
|
||
local old_target = caster
|
||
|
||
--生成独立的字符串
|
||
local str = DoUniqueString(ability:GetAbilityName())
|
||
print("Catapult:"..str)
|
||
|
||
--假设一个马甲
|
||
local unit = {}
|
||
|
||
--绑定信息
|
||
--是否发射下一个投射物
|
||
unit.CatapultNext = false
|
||
unit.count_num = 0
|
||
--本次弹射标识的字符串
|
||
unit.CatapultThisProjectile = str
|
||
unit.old_target = old_target
|
||
--本次弹射的目标
|
||
unit.CatapultThisTarget = target
|
||
|
||
--CatapultUnit用来存储unit
|
||
if caster.CatapultUnit == nil then
|
||
caster.CatapultUnit = {}
|
||
end
|
||
|
||
--把unit插入CatapultUnit
|
||
table.insert(caster.CatapultUnit,unit)
|
||
|
||
--用于决定是否发射投射物
|
||
local fire = true
|
||
|
||
--弹射最大次数
|
||
local count_num = 0
|
||
|
||
GameRules:GetGameModeEntity():SetContextThink(str,
|
||
function( )
|
||
|
||
--满足达到最大弹射次数删除计时器
|
||
if count_num>=count then
|
||
print("Catapult impact :"..count_num)
|
||
print("Catapult:"..str.." is over")
|
||
return nil
|
||
end
|
||
|
||
|
||
if unit.CatapultNext then
|
||
|
||
--获取单位组
|
||
local group = THTD_FindUnitsInRadius(caster,target:GetOrigin(),radius)
|
||
|
||
--用于计算循环次数
|
||
local num = 0
|
||
for i=1,#group do
|
||
if group[i].CatapultImpact == nil then
|
||
group[i].CatapultImpact = {}
|
||
end
|
||
|
||
--判断是否命中
|
||
local impact = CatapultFindImpact(group[i],str)
|
||
|
||
if impact == false then
|
||
|
||
--替换old_target
|
||
old_target = target
|
||
|
||
--新target
|
||
target = group[i]
|
||
|
||
--可以发射新投射物
|
||
fire = true
|
||
unit.count_num = count_num
|
||
--等待下一个目标
|
||
unit.old_target = old_target
|
||
unit.CatapultNext =false
|
||
|
||
--锁定当前目标
|
||
unit.CatapultThisTarget = target
|
||
break
|
||
end
|
||
num = num + 1
|
||
end
|
||
|
||
--如果大于等于单位组的数量那么就删除计时器
|
||
if num >= #group then
|
||
--从CatapultUnit中删除unit
|
||
TableRemoveSubTable(caster.CatapultUnit,unit)
|
||
|
||
print("Catapult impact :"..count_num)
|
||
print("Catapult:"..str.." is over")
|
||
return nil
|
||
end
|
||
end
|
||
|
||
--发射投射物
|
||
if fire then
|
||
fire = false
|
||
count_num = count_num + 1
|
||
local info =
|
||
{
|
||
Target = target,
|
||
Source = old_target,
|
||
Ability = ability,
|
||
EffectName = effectName,
|
||
bDodgeable = false,
|
||
iMoveSpeed = move_speed,
|
||
bProvidesVision = true,
|
||
iVisionRadius = 300,
|
||
iVisionTeamNumber = caster:GetTeamNumber(),
|
||
iSourceAttachment = DOTA_PROJECTILE_ATTACHMENT_ATTACK_1
|
||
}
|
||
projectile = ProjectileManager:CreateTrackingProjectile(info)
|
||
end
|
||
|
||
return 0.05
|
||
end,0)
|
||
end
|
||
|
||
--此函数在KV里面用OnProjectileHitUnit调用
|
||
function CatapultImpact( keys )
|
||
local caster = keys.caster
|
||
local target = keys.target
|
||
|
||
--防止意外
|
||
if caster.CatapultUnit == nil then
|
||
caster.CatapultUnit = {}
|
||
end
|
||
if target.CatapultImpact == nil then
|
||
target.CatapultImpact = {}
|
||
end
|
||
|
||
--挨个检测是否是弹射的目标
|
||
for i,v in pairs(caster.CatapultUnit) do
|
||
|
||
if v.CatapultThisProjectile ~= nil and v.CatapultThisTarget ~= nil then
|
||
|
||
if v.CatapultThisTarget == target then
|
||
|
||
--标记target被CatapultThisProjectile命中
|
||
table.insert(target.CatapultImpact,v.CatapultThisProjectile)
|
||
|
||
--允许发射下一次投射物
|
||
v.CatapultNext = true
|
||
return
|
||
end
|
||
|
||
end
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
function ParticleManager:DestroyParticleSystem(effectIndex, bool)
|
||
if effectIndex==nil then return end
|
||
if bool == true then
|
||
ParticleManager:DestroyParticle(effectIndex,true)
|
||
ParticleManager:ReleaseParticleIndex(effectIndex)
|
||
else
|
||
Timer.Wait 'Effect_Destroy_Particle' (4,
|
||
function()
|
||
ParticleManager:DestroyParticle(effectIndex,true)
|
||
ParticleManager:ReleaseParticleIndex(effectIndex)
|
||
end
|
||
)
|
||
end
|
||
end
|
||
|
||
function ParticleManager:DestroyParticleSystemTime(effectIndex,time)
|
||
if effectIndex==nil then return end
|
||
Timer.Wait 'Effect_Destroy_Particle_Time' (time,
|
||
function()
|
||
ParticleManager:DestroyParticle(effectIndex,true)
|
||
ParticleManager:ReleaseParticleIndex(effectIndex)
|
||
end
|
||
)
|
||
end
|
||
|
||
function ParticleManager:DestroyParticleSystemTimeFalse(effectIndex,time)
|
||
if effectIndex==nil then return end
|
||
Timer.Wait 'Effect_Destroy_Particle_Time' (time,
|
||
function()
|
||
ParticleManager:DestroyParticle(effectIndex,false)
|
||
ParticleManager:DestroyParticleSystem(effectIndex,false)
|
||
end
|
||
)
|
||
end
|
||
|
||
function ParticleManager:DestroyLinearProjectileSystem(effectIndex,bool)
|
||
if effectIndex==nil then return end
|
||
if(bool)then
|
||
ProjectileManager:DestroyLinearProjectile(effectIndex)
|
||
else
|
||
Timer.Wait 'Effect_Destroy_Particle' (8,
|
||
function()
|
||
if effectIndex==nil then return end
|
||
ProjectileManager:DestroyLinearProjectile(effectIndex)
|
||
end
|
||
)
|
||
end
|
||
end
|
||
|
||
function ParticleManager:DestroyLinearProjectileSystemTime(effectIndex,time)
|
||
if effectIndex==nil then return end
|
||
Timer.Wait 'Effect_Destroy_Particle_Time' (time,
|
||
function()
|
||
if effectIndex==nil then return end
|
||
ProjectileManager:DestroyLinearProjectile(effectIndex)
|
||
end
|
||
)
|
||
end
|
||
|
||
|
||
function CreateProjectileMoveToTargetPoint(projectileTable,caster,speed,acceleration1,acceleration2,func)
|
||
local effectIndex = ParticleManager:CreateParticle(projectileTable.EffectName, PATTACH_CUSTOMORIGIN, nil)
|
||
ParticleManager:SetParticleControlForward(effectIndex,3,projectileTable.vVelocity:Normalized())
|
||
|
||
local acceleration = acceleration1
|
||
local ability = projectileTable.Ability
|
||
local targets = {}
|
||
local targets_remove = {}
|
||
local dis = 0
|
||
local reflexCount = 0
|
||
local pString = DoUniqueString("projectile_string")
|
||
local high = projectileTable.vSpawnOriginNew.z - GetGroundHeight(projectileTable.vSpawnOriginNew, nil)
|
||
local fixedTime = 10.0
|
||
local count = 0
|
||
|
||
caster:SetContextThink(DoUniqueString("ability_caster_projectile"),
|
||
function()
|
||
if GameRules:IsGamePaused() then return 0.03 end
|
||
|
||
local vec = projectileTable.vSpawnOriginNew + projectileTable.vVelocity*speed/50
|
||
dis = dis + speed/50
|
||
|
||
-- 是否反射
|
||
if projectileTable.bReflexByBlock~=nil and projectileTable.bReflexByBlock==true then
|
||
--if GridNav:CanFindPath(projectileTable.vSpawnOriginNew,projectileTable.vSpawnOriginNew + projectileTable.vVelocity*speed/50)==false or GetHitCustomBlock(projectileTable.vSpawnOriginNew,projectileTable.vSpawnOriginNew + projectileTable.vVelocity*speed/50)~=nil then
|
||
if GetHitCustomBlock(projectileTable.vSpawnOriginNew,projectileTable.vSpawnOriginNew + projectileTable.vVelocity*speed/50)~=nil then
|
||
local inRad = GetRadBetweenTwoVec2D(vec,projectileTable.vSpawnOriginNew)
|
||
local blockRad = GetBlockTurning(inRad,projectileTable.vSpawnOriginNew)
|
||
projectileTable.vVelocity = Vector(math.cos(inRad-blockRad+math.pi),math.sin(inRad-blockRad+math.pi),0)
|
||
vec = projectileTable.vSpawnOriginNew + projectileTable.vVelocity*speed/50
|
||
ParticleManager:SetParticleControlForward(effectIndex,3,projectileTable.vVelocity:Normalized())
|
||
reflexCount = reflexCount + 1
|
||
for k,v in pairs(targets_remove) do
|
||
if v:GetContext(pString)~=0 then
|
||
v:SetContextNum(pString,0,0)
|
||
table.remove(targets_remove,k)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
-- 是否判断击中墙壁
|
||
if projectileTable.bStopByBlock~=nil and projectileTable.bStopByBlock==true then
|
||
if GridNav:CanFindPath(projectileTable.vSpawnOriginNew,projectileTable.vSpawnOriginNew + projectileTable.vVelocity*speed/50)==false then
|
||
if(projectileTable.bDeleteOnHit)then
|
||
if func then func(nil,vec) end
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
else
|
||
if func then func(nil,vec) end
|
||
end
|
||
end
|
||
end
|
||
|
||
targets = THTD_FindUnitsInRadius(caster,vec,projectileTable.fStartRadius)
|
||
|
||
if(targets[1]~=nil)then
|
||
if(projectileTable.bDeleteOnHit)then
|
||
if func then func(targets[1],vec,reflexCount) end
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
elseif(projectileTable.bDeleteOnHit==false)then
|
||
for k,v in pairs(targets) do
|
||
if v:GetContext(pString)~=1 then
|
||
v:SetContextNum(pString,1,0)
|
||
table.insert(targets_remove,v)
|
||
if func then func(v,vec,reflexCount) end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if(speed <= 0 and acceleration2 ~= 0)then
|
||
acceleration = acceleration2
|
||
speed = 0
|
||
acceleration2 = 0
|
||
end
|
||
|
||
fixedTime = fixedTime - 0.02
|
||
if(dis<projectileTable.fDistance and fixedTime > 0)then
|
||
ParticleManager:SetParticleControl(effectIndex,3,Vector(vec.x,vec.y,GetGroundHeight(vec, nil)+high))
|
||
projectileTable.vSpawnOriginNew = vec
|
||
speed = speed + acceleration
|
||
return 0.02
|
||
else
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
end
|
||
end,
|
||
0.02)
|
||
end
|
||
|
||
function CreateProjectileMoveToPoint(projectileTable,caster,targetPoint,speed,iVelocity,acceleration,func)
|
||
local effectIndex = ParticleManager:CreateParticle(projectileTable.EffectName, PATTACH_CUSTOMORIGIN, nil)
|
||
ParticleManager:SetParticleControlForward(effectIndex,3,(projectileTable.vVelocity*iVelocity/50 + speed/50 * (targetPoint - caster:GetOrigin()):Normalized()):Normalized())
|
||
|
||
local ability = projectileTable.Ability
|
||
local targets = {}
|
||
local targets_remove = {}
|
||
local totalDistance = 0
|
||
|
||
caster:SetContextThink(DoUniqueString("ability_caster_projectile"),
|
||
function()
|
||
if GameRules:IsGamePaused() then return 0.03 end
|
||
|
||
-- 向心力单位向量
|
||
local vecCentripetal = (projectileTable.vSpawnOriginNew - targetPoint):Normalized()
|
||
|
||
-- 向心力
|
||
local forceCentripetal = speed/50
|
||
|
||
-- 初速度单位向量
|
||
local vecInVelocity = projectileTable.vVelocity
|
||
|
||
-- 初始力
|
||
local forceIn = iVelocity/50
|
||
|
||
-- 投射物矢量
|
||
local vecProjectile = vecInVelocity * forceIn + forceCentripetal * vecCentripetal
|
||
|
||
local vec = projectileTable.vSpawnOriginNew + vecProjectile
|
||
|
||
-- 投射物单位向量
|
||
local particleForward = vecProjectile:Normalized()
|
||
|
||
-- 目标和投射物距离
|
||
local dis = GetDistanceBetweenTwoVec2D(targetPoint,vec)
|
||
|
||
ParticleManager:SetParticleControlForward(effectIndex,3,particleForward)
|
||
|
||
totalDistance = totalDistance + math.sqrt(forceIn*forceIn + forceCentripetal*forceCentripetal)
|
||
|
||
if(dis<projectileTable.fEndRadius)then
|
||
if(projectileTable.bDeleteOnHit)then
|
||
if func then func(projectileTable.vSpawnOriginNew) end
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
end
|
||
end
|
||
|
||
if(totalDistance<projectileTable.fDistance and dis>=projectileTable.fEndRadius)then
|
||
ParticleManager:SetParticleControl(effectIndex,3,vec)
|
||
projectileTable.vSpawnOriginNew = vec
|
||
speed = speed + acceleration
|
||
return 0.02
|
||
else
|
||
if func then func(projectileTable.vSpawnOriginNew) end
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
end
|
||
end,
|
||
0.02)
|
||
end
|
||
|
||
function CreateProjectileMoveToTarget(projectileTable,caster,target,speed,iVelocity,acceleration,func)
|
||
local effectIndex = ParticleManager:CreateParticle(projectileTable.EffectName, PATTACH_CUSTOMORIGIN, nil)
|
||
ParticleManager:SetParticleControlForward(effectIndex,3,(projectileTable.vVelocity*iVelocity/50 + speed/50 * (target:GetOrigin() - caster:GetOrigin()):Normalized()):Normalized())
|
||
|
||
local ability = projectileTable.Ability
|
||
local targets = {}
|
||
local targets_remove = {}
|
||
local totalDistance = 0
|
||
|
||
caster:SetContextThink(DoUniqueString("ability_caster_projectile"),
|
||
function()
|
||
if GameRules:IsGamePaused() then return 0.03 end
|
||
if target:IsNull() or target==nil then
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
end
|
||
|
||
-- 向心力单位向量
|
||
local vecCentripetal = (projectileTable.vSpawnOriginNew - target:GetOrigin()):Normalized()
|
||
|
||
-- 向心力
|
||
local forceCentripetal = speed/50
|
||
|
||
-- 初速度单位向量
|
||
local vecInVelocity = projectileTable.vVelocity
|
||
|
||
-- 初始力
|
||
local forceIn = iVelocity/50
|
||
|
||
-- 投射物矢量
|
||
local vecProjectile = vecInVelocity * forceIn + forceCentripetal * vecCentripetal
|
||
|
||
local vec = projectileTable.vSpawnOriginNew + vecProjectile
|
||
|
||
-- 投射物单位向量
|
||
local particleForward = vecProjectile:Normalized()
|
||
|
||
-- 目标和投射物距离
|
||
local dis = GetDistanceBetweenTwoVec2D(target:GetOrigin(),vec)
|
||
|
||
ParticleManager:SetParticleControlForward(effectIndex,3,particleForward)
|
||
|
||
totalDistance = totalDistance + math.sqrt(forceIn*forceIn + forceCentripetal*forceCentripetal)
|
||
|
||
if dis < projectileTable.fStartRadius then
|
||
targets = THTD_FindUnitsInRadius(caster,vec,projectileTable.fStartRadius)
|
||
|
||
if(targets[1]~=nil)then
|
||
if(projectileTable.bDeleteOnHit)then
|
||
if func then func(targets[1],projectileTable.vSpawnOriginNew) end
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
else
|
||
if #targets_remove==0 then
|
||
table.insert(targets_remove,targets[1])
|
||
if func then func(targets[1],projectileTable.vSpawnOriginNew) end
|
||
else
|
||
for k,v in pairs(targets) do
|
||
for k1,v1 in pairs(targets_remove) do
|
||
if v~=v1 then
|
||
table.insert(targets_remove,v)
|
||
if func then func(v,projectileTable.vSpawnOriginNew) end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
if(totalDistance<projectileTable.fDistance and dis>=projectileTable.fEndRadius)then
|
||
ParticleManager:SetParticleControl(effectIndex,3,vec)
|
||
projectileTable.vSpawnOriginNew = vec
|
||
speed = speed + acceleration
|
||
return 0.02
|
||
else
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
end
|
||
end,
|
||
0.02)
|
||
end
|
||
|
||
function CreateProjectileThrowToTargetPoint(projectileTable,caster,targetPoint,speed,upSpeed,func)
|
||
local ability = projectileTable.Ability
|
||
local targets = {}
|
||
local targets_remove = {}
|
||
local totalDistance = 0
|
||
|
||
-- 过程时间
|
||
local distance = GetDistanceBetweenTwoVec2D(caster:GetOrigin(),targetPoint)
|
||
local t = distance/speed
|
||
|
||
-- 重力方向
|
||
local vecGravity = Vector(0,0,-1)
|
||
|
||
local PV = targetPoint.z - 128 - caster:GetOrigin().z
|
||
|
||
-- 重力大小
|
||
local gravity = 0.02 * ((2*upSpeed+PV/16)/t)
|
||
|
||
-- 初速度单位向量
|
||
local vecInVelocity = projectileTable.vVelocity
|
||
|
||
-- 初始水平方向力
|
||
local forceIn = 0.02 * speed
|
||
|
||
-- 投射物矢量
|
||
local vecProjectile = vecInVelocity * forceIn + gravity * vecGravity
|
||
|
||
-- 投射物单位向量
|
||
local particleForward = vecProjectile:Normalized()
|
||
|
||
local effectIndex = ParticleManager:CreateParticle(projectileTable.EffectName, PATTACH_CUSTOMORIGIN, nil)
|
||
ParticleManager:SetParticleControlForward(effectIndex,3,caster:GetForwardVector())
|
||
|
||
local count = 0
|
||
|
||
caster:SetContextThink(DoUniqueString("ability_caster_projectile"),
|
||
function()
|
||
if GameRules:IsGamePaused() then return 0.03 end
|
||
|
||
distance = distance - forceIn
|
||
if distance<0 then
|
||
forceIn = 0
|
||
end
|
||
|
||
-- 投射物矢量
|
||
vecProjectile = vecInVelocity * forceIn + upSpeed * vecGravity
|
||
|
||
local vec = projectileTable.vSpawnOriginNew + vecProjectile
|
||
|
||
totalDistance = totalDistance + math.sqrt(forceIn*forceIn + gravity*gravity)
|
||
|
||
if(projectileTable.vSpawnOriginNew.z+128>targetPoint.z or t > t/2)then
|
||
ParticleManager:SetParticleControl(effectIndex,3,vec)
|
||
projectileTable.vSpawnOriginNew = vec
|
||
upSpeed = upSpeed - gravity
|
||
t = t - 0.02
|
||
return 0.02
|
||
else
|
||
targets = THTD_FindUnitsInRadius(caster,vec,projectileTable.fStartRadius)
|
||
if func then func(targets) end
|
||
ParticleManager:DestroyParticleSystem(effectIndex,true)
|
||
return nil
|
||
end
|
||
end,
|
||
0.02)
|
||
end
|
||
|
||
function GetBlockTurning(face,pos)
|
||
local vface = face
|
||
|
||
local hitblock = GetHitCustomBlock(pos,pos + Vector(math.cos(vface/180*math.pi),math.sin(vface/180*math.pi),0)*50)
|
||
|
||
if hitblock~=nil then
|
||
return 90
|
||
end
|
||
return 90
|
||
end
|
||
|
||
function GetBlockHeight(rad,pos)
|
||
for i=0,200 do
|
||
local gridPos = pos + Vector(math.cos(rad),math.sin(rad),0)*i
|
||
local height = GetGroundHeight(gridPos,nil)
|
||
if height > pos.z + 128 then
|
||
return height
|
||
end
|
||
end
|
||
return 0
|
||
end
|
||
|
||
function GetHitCustomBlock(vec,pos)
|
||
for k,v in pairs(THTD_Custom_Hit_Block) do
|
||
local circlePoint = v.circlePoint
|
||
local dis1 = GetDistanceBetweenTwoVec2D(vec,circlePoint)
|
||
local dis2 = GetDistanceBetweenTwoVec2D(pos,circlePoint)
|
||
|
||
if dis1-50 < v.radius and dis2+50 >= v.radius then
|
||
return v
|
||
end
|
||
end
|
||
return nil
|
||
end
|
||
|
||
function IsAroundBlock(pos)
|
||
for k,v in pairs(THTD_Custom_Hit_Block) do
|
||
local circlePoint = v.circlePoint
|
||
local dis = GetDistanceBetweenTwoVec2D(pos,circlePoint)
|
||
|
||
if dis < v.radius then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
function clone(object)
|
||
local lookup_table = {}
|
||
local function _copy(object)
|
||
if type(object) ~= "table" then
|
||
return object
|
||
elseif lookup_table[object] then
|
||
return lookup_table[object]
|
||
end
|
||
local newObject = {}
|
||
lookup_table[object] = newObject
|
||
for key, value in pairs(object) do
|
||
newObject[_copy(key)] = _copy(value)
|
||
end
|
||
return setmetatable(newObject, getmetatable(object))
|
||
end
|
||
return _copy(object)
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
--[[
|
||
计时器函数Timer
|
||
调用方法:
|
||
Timer.Wait '5秒后打印一次' (5,
|
||
function()
|
||
print '我已经打印了一次文本'
|
||
end
|
||
)
|
||
|
||
Timer.Loop '每隔1秒打印一次,一共打印5次' (1, 5,
|
||
function(i)
|
||
print('这是第' .. i .. '次打印')
|
||
if i == 5 then
|
||
print('我改变主意了,我还要打印10次,但是间隔降低为0.5秒')
|
||
return 0.5, i + 10
|
||
end
|
||
if i == 10 then
|
||
print('我好像打印的太多了,算了不打印了')
|
||
return true
|
||
end
|
||
end
|
||
)
|
||
]]
|
||
|
||
--全局计时器表
|
||
Timer = {}
|
||
local Timer = Timer
|
||
setmetatable(Timer, Timer)
|
||
|
||
function Timer.Wait(name)
|
||
--[[if not dota_base_game_mode then
|
||
print('WARNING: Timer created too soon!')
|
||
return
|
||
end]]--
|
||
|
||
return function(t, func)
|
||
local ent = GameRules:GetGameModeEntity()
|
||
ent:SetThink(func, DoUniqueString(name), t)
|
||
end
|
||
end
|
||
|
||
function Timer.Loop(name)
|
||
--[[if not dota_base_game_mode then
|
||
print('WARNING: Timer created too soon!')
|
||
return
|
||
end]]--
|
||
|
||
return function(t, count, func)
|
||
if not func then
|
||
count, func = -1, count
|
||
end
|
||
|
||
local times = 0
|
||
local function func2()
|
||
times = times + 1
|
||
local t2, count2 = func(times)
|
||
t, count = t2 or t, count2 or count
|
||
|
||
if t == true or times == count then
|
||
return nil
|
||
end
|
||
|
||
return t
|
||
end
|
||
|
||
local ent = GameRules:GetGameModeEntity()
|
||
ent:SetThink(func2, DoUniqueString(name), t)
|
||
end
|
||
end
|
||
|
||
TIMERS_THINK = 0.01
|
||
|
||
if Timers == nil then
|
||
print ( '[Timers] creating Timers' )
|
||
Timers = {}
|
||
Timers.__index = Timers
|
||
end
|
||
|
||
function Timers:new( o )
|
||
o = o or {}
|
||
setmetatable( o, Timers )
|
||
return o
|
||
end
|
||
|
||
function Timers:start()
|
||
Timers = self
|
||
self.timers = {}
|
||
|
||
local ent = Entities:CreateByClassname("info_target") -- Entities:FindByClassname(nil, 'CWorld')
|
||
ent:SetThink("Think", self, "timers", TIMERS_THINK)
|
||
end
|
||
|
||
function Timers:Think()
|
||
if GameRules:State_Get() >= DOTA_GAMERULES_STATE_POST_GAME then
|
||
return
|
||
end
|
||
|
||
-- Track game time, since the dt passed in to think is actually wall-clock time not simulation time.
|
||
local now = GameRules:GetGameTime()
|
||
|
||
-- Process timers
|
||
for k,v in pairs(Timers.timers) do
|
||
local bUseGameTime = true
|
||
if v.useGameTime ~= nil and v.useGameTime == false then
|
||
bUseGameTime = false
|
||
end
|
||
local bOldStyle = false
|
||
if v.useOldStyle ~= nil and v.useOldStyle == true then
|
||
bOldStyle = true
|
||
end
|
||
|
||
local now = GameRules:GetGameTime()
|
||
if not bUseGameTime then
|
||
now = Time()
|
||
end
|
||
|
||
if v.endTime == nil then
|
||
v.endTime = now
|
||
end
|
||
-- Check if the timer has finished
|
||
if now >= v.endTime then
|
||
-- Remove from timers list
|
||
Timers.timers[k] = nil
|
||
|
||
-- Run the callback
|
||
local status, nextCall = pcall(v.callback, GameRules:GetGameModeEntity(), v)
|
||
|
||
-- Make sure it worked
|
||
if status then
|
||
-- Check if it needs to loop
|
||
if nextCall then
|
||
-- Change its end time
|
||
|
||
if bOldStyle then
|
||
v.endTime = v.endTime + nextCall - now
|
||
else
|
||
v.endTime = v.endTime + nextCall
|
||
end
|
||
|
||
Timers.timers[k] = v
|
||
end
|
||
|
||
-- Update timer data
|
||
--self:UpdateTimerData()
|
||
else
|
||
-- Nope, handle the error
|
||
Timers:HandleEventError('Timer', k, nextCall)
|
||
end
|
||
end
|
||
end
|
||
return TIMERS_THINK
|
||
end
|
||
|
||
function Timers:HandleEventError(name, event, err)
|
||
print(err)
|
||
|
||
-- Ensure we have data
|
||
name = tostring(name or 'unknown')
|
||
event = tostring(event or 'unknown')
|
||
err = tostring(err or 'unknown')
|
||
|
||
-- Tell everyone there was an error
|
||
--Say(nil, name .. ' threw an error on event '..event, false)
|
||
--Say(nil, err, false)
|
||
|
||
-- Prevent loop arounds
|
||
if not self.errorHandled then
|
||
-- Store that we handled an error
|
||
self.errorHandled = true
|
||
end
|
||
end
|
||
|
||
function Timers:CreateTimer(name, args)
|
||
if type(name) == "function" then
|
||
args = {callback = name}
|
||
name = DoUniqueString("timer")
|
||
elseif type(name) == "table" then
|
||
args = name
|
||
name = DoUniqueString("timer")
|
||
elseif type(name) == "number" then
|
||
args = {endTime = name, callback = args}
|
||
name = DoUniqueString("timer")
|
||
end
|
||
if not args.callback then
|
||
print("Invalid timer created: "..name)
|
||
return
|
||
end
|
||
|
||
|
||
local now = GameRules:GetGameTime()
|
||
if args.useGameTime ~= nil and args.useGameTime == false then
|
||
now = Time()
|
||
end
|
||
|
||
if args.endTime == nil then
|
||
args.endTime = now
|
||
elseif args.useOldStyle == nil or args.useOldStyle == false then
|
||
args.endTime = now + args.endTime
|
||
end
|
||
|
||
Timers.timers[name] = args
|
||
end
|
||
|
||
function Timers:RemoveTimer(name)
|
||
Timers.timers[name] = nil
|
||
end
|
||
|
||
function Timers:RemoveTimers(killAll)
|
||
local timers = {}
|
||
|
||
if not killAll then
|
||
for k,v in pairs(Timers.timers) do
|
||
if v.persist then
|
||
timers[k] = v
|
||
end
|
||
end
|
||
end
|
||
|
||
Timers.timers = timers
|
||
end
|
||
|
||
Timers:start()
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
function PrecacheEveryThingFromKV( context )
|
||
local kv_files = {
|
||
"scripts/npc/npc_units_custom.txt",
|
||
--[["scripts/npc/npc_abilities_custom.txt",
|
||
"scripts/npc/npc_heroes_custom.txt",
|
||
"scripts/npc/npc_abilities_override.txt",
|
||
"npc_items_custom.txt"]]--
|
||
}
|
||
for _, kv in pairs(kv_files) do
|
||
local kvs = LoadKeyValues(kv)
|
||
if kvs then
|
||
-- print("BEGIN TO PRECACHE RESOURCE FROM: ", kv)
|
||
PrecacheEverythingFromTable( context, kvs)
|
||
end
|
||
end
|
||
local unitKv = LoadKeyValues("scripts/npc/npc_units_custom.txt")
|
||
for k,v in pairs(unitKv) do
|
||
-- print("PRECACHE UNIT RESOURCE: "..k, k)
|
||
PrecacheUnitByNameSync(k,context)
|
||
end
|
||
end
|
||
|
||
function PrecacheEverythingFromTable( context, kvtable)
|
||
for key, value in pairs(kvtable) do
|
||
if type(value) == "table" then
|
||
PrecacheEverythingFromTable( context, value )
|
||
else
|
||
if string.find(value, "vpcf") then
|
||
PrecacheResource( "particle", value, context)
|
||
-- print("PRECACHE PARTICLE RESOURCE", value)
|
||
end
|
||
if string.find(value, "vmdl") then
|
||
PrecacheResource( "model", value, context)
|
||
-- print("PRECACHE MODEL RESOURCE", value)
|
||
end
|
||
if string.find(value, "vsndevts") then
|
||
PrecacheResource( "soundfile", value, context)
|
||
-- print("PRECACHE SOUND RESOURCE", value)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--------------------------------------------------------------------------------
|
||
|
||
-- 当物品被摧毁时执行,出现场景为升星、培养和出售,以及程序删除,将物品返回卡池,如果是卡牌则同时摧毁关联的塔
|
||
-- 参数:物品实体,玩家id,是否出售场景(不需要在本函数中进行删除物品)
|
||
function OnItemDestroyed(caster, item, isSold)
|
||
--摧毁关联的塔
|
||
local tower = item:THTD_GetTower()
|
||
if tower ~= nil then
|
||
tower:ForceKill(false)
|
||
end
|
||
|
||
local itemName = item:GetAbilityName()
|
||
--炸弹和福弹及赠送的不返回卡池
|
||
if itemName ~= "item_2001" and item.card_poor_player_id ~= nil then
|
||
THTD_AddItemToListByName(item.card_poor_player_id, itemName)
|
||
end
|
||
|
||
if isSold ~= true then
|
||
item:RemoveSelf()
|
||
end
|
||
end
|
||
|
||
-- 同步卡池
|
||
function SetNetTableTowerPlayerList(playerId)
|
||
local steamid = PlayerResource:GetSteamID(playerId)
|
||
CustomNetTables:SetTableValue("TowerListInfo", "cardlist"..tostring(steamid), towerPlayerList[playerId+1])
|
||
end
|
||
|
||
-- 同步当前塔清单
|
||
function SetNetTableTowerList(caster)
|
||
local hero = GameRules.HeroList[caster:GetPlayerOwnerID()]
|
||
if hero == nil then return end
|
||
|
||
local towerList = {}
|
||
for k,v in pairs(hero.thtd_hero_tower_list) do
|
||
towerList[k] = v:GetEntityIndex()
|
||
end
|
||
local steamid = PlayerResource:GetSteamID(hero:GetPlayerOwnerID())
|
||
CustomNetTables:SetTableValue("TowerListInfo", "towerlist"..tostring(steamid), towerList)
|
||
end
|
||
|
||
-- 获取现实时间,格式为: 2019-04-19 10:00:00
|
||
function GetRealDateTime()
|
||
local date = GetSystemDate() --04/12/19 月 日 年
|
||
local time = GetSystemTime() --00:10:43 时 分 秒
|
||
return "20"..string.sub(date,7,8).."-"..string.sub(date,1,2).."-"..string.sub(date,4,5).." "..time
|
||
end
|
||
|
||
-- 判断当前时间是否超过了指定时间,格式必须为 2019-04-19 10:00:00
|
||
function IsEndByDateTime(endDateTime)
|
||
local currentDateTime = GetRealDateTime()
|
||
if GameRules.GameData.server_time ~= "" then
|
||
currentDateTime = GameRules.GameData.server_time
|
||
end
|
||
if currentDateTime > endDateTime then
|
||
return true
|
||
else
|
||
return false
|
||
end
|
||
end
|