Добавлен nazarpunk,
опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
Zinc
Тип:
Способность
Версия Warcraft:
1.26+
Blink Strike
MUI: да
Импорт: иконка
Утечки: нет
Требования: JNGP
Описание: Герой телепортируется к каждой цели на своём пути и наносит ей 100*уровень заклинания магического урона.
Импорт: иконка
Утечки: нет
Требования: JNGP
Описание: Герой телепортируется к каждой цели на своём пути и наносит ей 100*уровень заклинания магического урона.
- Имеет баф, для работы с другими заклинаниями
- Прерывается оглушением и приказами игрока
- Учитывает неуязвимость к магии
- Гнев деревьев блокирует заклинание
- Не использует даммикаст
- Не паузит героя
- Использует хэштаблицу для хранения id таймера
Технические подробности
Перенос в свою карту
Способности
- 'AEbs' Blink Strike (герой) - способность для героя
- 'ABbs' Blink Strike (бафф) - способность для наложения бафа
Заклинания/Эффекты
- 'BEbs' Blink Strike - бафф способности
Триггеры
- SpellBlinkStrike
Импорт
- ReplaceableTextures\CommandButtons\BTNBlinkStrike.blp
- ReplaceableTextures\CommandButtonsDisabled\DISBTNBlinkStrike.blp
- ReplaceableTextures\PassiveButtons\PASBlinkStrike.blp
- ReplaceableTextures\CommandButtonsDisabled\DISPASBlinkStrike.blp
Настройка
constant integer AbilityID = 'AEbs'; // Равкод способности
constant integer AbilityBuffID = 'ABbs'; // Равкод способности для баффа
constant integer BuffID = 'BEbs'; // Равкод баффа
constant integer PathItemID = 'wolg'; // Предмет для нахождения финальной точки
constant real TimerPeriod = 0.25; // Время между блинками: 1/4 секунды.
constant integer DamageDistance = 64; // Расстояние захвата юнита
constant string EffectCasterHandsPath = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"; // Еффект, который крепится к "hand left" и "hand right"
constant string EffectBlinkTarget = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl"; // Еффект блинка для точки
constant string EffectBlinkCaster = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"; // Еффект блинка для кастера
constant string CasterAnimationName = "Attack 2"; // Анимация при ударе
constant real CasterAnimationDuration = 0.9; // Длительность анимации при ударе у модели
constant integer CasterOpacity = 150; // Прозрачность кастера на момент заклинания 0 - 255
constant real CasterAnimationScale = CasterAnimationDuration/TimerPeriod*1; // Не трогать
hashtable HT = InitHashtable(); // Хэштаблица для таймера
// Можете вписать туда вашу таблицу, например:
// hashtable HT = udg_HashTable;
// Текстаг над целью. Настроен на эмуляцию критического удара
function addTextTag(widget target, string text) {
real x = GetWidgetX(target);
real y = GetWidgetY(target);
texttag tt = CreateTextTag();
SetTextTagText(tt, text+"!", 0.024);
SetTextTagPos(tt, x, y, 0.0);
SetTextTagColor(tt, 255, 0, 0, 255);
SetTextTagVelocity(tt, 0.0, 0.04);
SetTextTagFadepoint(tt, 2.0);
SetTextTagLifespan(tt, 5.0);
SetTextTagVisibility(tt, IsVisibleToPlayer(x, y, GetLocalPlayer()));
SetTextTagPermanent(tt, false);
tt = null;
}
// Нанесение урона по цели
function onDamage(unit caster, unit target, integer level) {
// caster - юнит, использующий способность
// target - цель
// level - уровень способности
real damage = level*100; // считаем урон от уровня способности
UnitDamageTarget(
caster,
target,
damage,
true, // является ли атакой
false, // является ли дальним боем
ATTACK_TYPE_MAGIC,
DAMAGE_TYPE_NORMAL,
WEAPON_TYPE_WHOKNOWS
);
addTextTag(target, I2S(R2I(damage)));
}
// Дальность заклинания
function getRange(integer level) -> integer {
// level - уровень способности
return 800 + level*100;
}
// Ограничить количество целей. Снять ограничение: -1;
function getTargetLimit(integer level) -> integer {
// level - уровень способности
return -1;
}
// Проверяем, возможно ли кастовать заклинание
function canBlink(unit caster) -> boolean {
// caster - юнит, использующий способность
return (
GetUnitAbilityLevel(caster, 'BEer') == 0 // Гнев деревьев
);
}
// Проверка целей
function checkTarget(unit caster, unit target) -> boolean {
// caster - юнит, использующий способность
// target - цель проверки
return (
caster != target // Не кастер
&&
!IsUnitType(target, UNIT_TYPE_STRUCTURE) // Не здание
&&
!IsUnitType(target, UNIT_TYPE_FLYING) // Не летающий
&&
!IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) // Восприимчив к магии
&&
IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(target)) // Враг
);
}
Код заклинания
//! zinc
library SpellBlinkStrike {
constant integer AbilityID = 'AEbs'; // Равкод способности
constant integer AbilityBuffID = 'ABbs'; // Равкод способности для баффа
constant integer BuffID = 'BEbs'; // Равкод баффа
constant integer PathItemID = 'wolg'; // Предмет для нахождения финальной точки
constant real TimerPeriod = 0.25; // Время между блинками: 1/4 секунды.
constant integer DamageDistance = 64; // Расстояние захвата юнита
constant string EffectCasterHandsPath = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"; // Еффект, который крепится к "hand left" и "hand right"
constant string EffectBlinkTarget = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl"; // Еффект блинка для точки
constant string EffectBlinkCaster = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"; // Еффект блинка для кастера
constant string CasterAnimationName = "Attack 2"; // Анимация при ударе
constant real CasterAnimationDuration = 0.9; // Длительность анимации при ударе у модели
constant integer CasterOpacity = 150; // Прозрачность кастера на момент заклинания 0 - 255
constant real CasterAnimationScale = CasterAnimationDuration/TimerPeriod*1; // Не трогать
hashtable HT = InitHashtable(); // Хэштаблица для таймера
// Можете вписать туда вашу таблицу, например:
// hashtable HT = udg_HashTable;
// Текстаг над целью. Настроен на эмуляцию критического удара
function addTextTag(widget target, string text) {
real x = GetWidgetX(target);
real y = GetWidgetY(target);
texttag tt = CreateTextTag();
SetTextTagText(tt, text+"!", 0.024);
SetTextTagPos(tt, x, y, 0.0);
SetTextTagColor(tt, 255, 0, 0, 255);
SetTextTagVelocity(tt, 0.0, 0.04);
SetTextTagFadepoint(tt, 2.0);
SetTextTagLifespan(tt, 5.0);
SetTextTagVisibility(tt, IsVisibleToPlayer(x, y, GetLocalPlayer()));
SetTextTagPermanent(tt, false);
tt = null;
}
// Нанесение урона по цели
function onDamage(unit caster, unit target, integer level) {
// caster - юнит, использующий способность
// target - цель
// level - уровень способности
real damage = level*100; // считаем урон от уровня способности
UnitDamageTarget(
caster,
target,
damage,
true, // является ли атакой
false, // является ли дальним боем
ATTACK_TYPE_MAGIC,
DAMAGE_TYPE_NORMAL,
WEAPON_TYPE_WHOKNOWS
);
addTextTag(target, I2S(R2I(damage)));
}
// Дальность заклинания
function getRange(integer level) -> integer {
// level - уровень способности
return 800 + level*100;
}
// Ограничить количество целей. Снять ограничение: -1;
function getTargetLimit(integer level) -> integer {
// level - уровень способности
return -1;
}
// Проверяем, возможно ли кастовать заклинание
function canBlink(unit caster) -> boolean {
// caster - юнит, использующий способность
return (
GetUnitAbilityLevel(caster, 'BEer') == 0 // Гнев деревьев
);
}
// Проверка целей
function checkTarget(unit caster, unit target) -> boolean {
// caster - юнит, использующий способность
// target - цель проверки
return (
caster != target // Не кастер
&&
!IsUnitType(target, UNIT_TYPE_STRUCTURE) // Не здание
&&
!IsUnitType(target, UNIT_TYPE_FLYING) // Не летающий
&&
!IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) // Восприимчив к магии
&&
IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(target)) // Враг
);
}
//
// Заклинание
//
// Polar - https://xgm.guru/p/wc3/polar
function GetPolarOffsetX(real x, real distance, real angle) -> real {
return x + distance * Cos(angle * bj_DEGTORAD);
}
function GetPolarOffsetY(real y, real distance, real angle) -> real {
return y + distance * Sin(angle * bj_DEGTORAD);
}
function DistanceBetweenCoords(real x1, real y1, real x2, real y2) -> real {
real dx = x2 - x1;
real dy = y2 - y1;
return SquareRoot(dx*dx + dy*dy);
}
function AngleBetweenCoords(real x1, real y1, real x2, real y2) -> real {
return bj_RADTODEG * Atan2(y2 - y1, x2 - x1);
}
function AngleDifference(real a1, real a2) -> real {
real x;
a1 = ModuloReal(a1, 360);
a2 = ModuloReal(a2, 360);
if (a1 > a2) {
x = a1;
a1 = a2;
a2 = x;
}
x = a2 - 360;
if (a2 - a1 > a1 - x){
a2 = x;
}
return RAbsBJ(a1 - a2);
}
function Perpendicular (real Xa, real Ya, real Xb, real Yb, real Xc, real Yc) -> real {
return SquareRoot((Xa - Xc) * (Xa - Xc) + (Ya - Yc) * (Ya - Yc)) * Sin(Atan2(Yc-Ya,Xc-Xa) - Atan2(Yb-Ya,Xb-Xa));
}
// endPolar
function isUnitAlive(unit target) -> boolean{
return GetWidgetLife(target) > 0.405;
}
struct data {
unit caster, target[8190];
integer targetI, targetC, limit, level, timerID;
real xc, yc, xt, yt, angle;
effect effectCaster[2];
boolean isCancel;
static method create(unit caster, real xt, real yt) -> data {
data this = data.allocate();
item it;
integer level = GetUnitAbilityLevel(caster, AbilityID);
real xc = GetUnitX(caster);
real yc = GetUnitY(caster);
real distance = DistanceBetweenCoords(xc, yc, xt, yt);
real angle = AngleBetweenCoords(xc, yc, xt, yt);
real range = I2R(getRange(level));
unit u, utemp;
integer i, j, imin;
timer t;
integer tid;
group g = CreateGroup();
if (distance > range){
xt = GetPolarOffsetX(xc, range, angle);
yt = GetPolarOffsetY(yc, range, angle);
}
range = RMinBJ(distance, range);
it = CreateItem(PathItemID, xt, yt);
this.isCancel = false;
this.level = level;
this.limit = getTargetLimit(this.level);
this.caster = caster;
this.xc = xc;
this.yc = yc;
this.xt = GetItemX(it);
this.yt = GetItemY(it);
RemoveItem(it); it = null;
this.angle = AngleBetweenCoords(this.xc, this.yc, this.xt, this.yt);
this.effectCaster[0] = AddSpecialEffectTarget(EffectCasterHandsPath, this.caster, "hand left");
this.effectCaster[1] = AddSpecialEffectTarget(EffectCasterHandsPath, this.caster, "hand right");
GroupEnumUnitsInRange(g, xc, yc, range + DamageDistance, Filter(function() -> boolean {
return isUnitAlive(GetFilterUnit());
}));
this.targetI = -1;
this.targetC = -1;
while(true){
u = FirstOfGroup(g);
if (u == null) {break;}
if (
checkTarget(this.caster, u)
&&
R2I(RAbsBJ(Perpendicular(this.xc, this.yc, this.xt, this.yt, GetUnitX(u), GetUnitY(u)))) <= DamageDistance
&&
AngleDifference(AngleBetweenCoords(this.xc, this.yc, this.xt, this.yt), AngleBetweenCoords(this.xc, this.yc, GetUnitX(u), GetUnitY(u))) < 60
){
this.targetI = this.targetI + 1;
this.target[targetI] = u;
}
GroupRemoveUnit(g, u);
}
DestroyGroup(g); g = null; u = null;
SetUnitPathing(this.caster, false);
SetUnitTimeScale(this.caster, CasterAnimationScale);
SetUnitVertexColor(this.caster, 255, 255, 255, CasterOpacity);
UnitAddAbility(this.caster, AbilityBuffID);
if (this.targetI >= 0){
t = CreateTimer();
tid = GetHandleId(t);
i = 0;
while(i < this.targetI){
imin = i;
j = i + 1;
while(j <= this.targetI){
if (
DistanceBetweenCoords(this.xc, this.yc, GetUnitX(this.target[j]), GetUnitY(this.target[j]))
<
DistanceBetweenCoords(this.xc, this.yc, GetUnitX(this.target[imin]), GetUnitY(this.target[imin]))
){
imin = j;
}
j = j + 1;
}
utemp = this.target[i];
this.target[i] = this.target[imin];
this.target[imin] = utemp;
i = i + 1;
}
SaveInteger(HT, tid, 0, this);
TimerStart(t, TimerPeriod, true, function data.callback);
} else {
this.destroy();
}
utemp = null;
t = null;
return this;
}
method destroy(){
integer i;
SetUnitPathing(this.caster, true);
UnitRemoveAbility(this.caster, AbilityBuffID);
UnitRemoveAbility(this.caster, BuffID);
if (!this.isCancel){
SetUnitX(this.caster, this.xt);
SetUnitY(this.caster, this.yt);
DestroyEffect(AddSpecialEffectTarget(EffectBlinkCaster, this.caster, "origin"));
}
SetUnitTimeScale(this.caster, 1);
if (isUnitAlive(this.caster)){
SetUnitAnimation(this.caster, "stand");
} else {
SetUnitAnimation(this.caster, "death");
}
SetUnitVertexColor(this.caster, 255, 255, 255, 255);
for(0 <= i <= 1){
DestroyEffect(this.effectCaster[i]);
this.effectCaster[i] = null;
}
for(0 <= i <= this.targetI){
this.target[i] = null;
}
this.caster = null;
this.deallocate();
}
static method callback(){
timer t = GetExpiredTimer();
integer tid = GetHandleId(t);
data this = LoadInteger(HT, tid, 0);
integer order = GetUnitCurrentOrder(this.caster);
this.targetC = this.targetC + 1;
if ((order != 0 && order != 851983 /* attack */ && order != 851972 /* stop */) || !isUnitAlive(this.caster)){
this.isCancel = true;
}
if (this.targetC > this.targetI || this.isCancel || (this.limit >= 0 && this.targetC >= this.limit)){
this.destroy();
PauseTimer(t);
DestroyTimer(t);
FlushChildHashtable(HT, tid);
} else {
if (isUnitAlive(this.target[targetC])){
onDamage(this.caster, this.target[targetC], this.level);
SetUnitX(this.caster, GetUnitX(this.target[targetC]));
SetUnitY(this.caster, GetUnitY(this.target[targetC]));
IssueImmediateOrderById(this.caster, 851972 /* stop */);
SetUnitAnimation(this.caster, CasterAnimationName);
}
this.target[targetC] = null;
}
t = null;
}
}
function onInit(){
integer i;
trigger t1 = CreateTrigger();
trigger t2 = CreateTrigger();
for (0 <= i < bj_MAX_PLAYER_SLOTS){
TriggerRegisterPlayerUnitEvent(t1, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null);
TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null);
}
TriggerAddCondition(t1, Filter(function() -> boolean {
if (GetSpellAbilityId() == AbilityID && GetUnitAbilityLevel(GetTriggerUnit(), AbilityBuffID) == 0){
DestroyEffect(AddSpecialEffect(EffectBlinkTarget, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit())));
}
return false;
}));
TriggerAddCondition(t2, Filter(function() -> boolean {
if (
GetSpellAbilityId() == AbilityID
&&
GetUnitAbilityLevel(GetTriggerUnit(), AbilityBuffID) == 0
&&
canBlink(GetTriggerUnit())
){
data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY());
}
return false;
}));
t1 = null; t2 = null;
}
}
//! endzinc
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Лично я считаю дурным тоном пихать 100500 сторонних либ в спеллы на показ + еще и сомнительные наработки.
NazarPunk, Насчет мемхака:
1 - ты сам себе осмысленно собрался выстрелить в ногу?
2 - если бы хотели, вы бы уже сидели и смотрели на надпись на экране "Перевидите 100usd на кошлек xxxxxxxxxxxxxxxxx, иначе вашим фалам хана!" а так это никому не интересно, потом написать уж прям тру вирус - не так уж и просто, потом "ЗАЧЕМ" - ? Ради потроллить пару человек сидеть и кодить много кода? Ну те кто страдает такими вот шуточками, на это попросту неспособен, а тот кто способен подобным никогда заниматься не будет.
quq_CCCP:
quq_CCCP:
Отредактирован PT153
quq_CCCP, поддерживаю, еще бы в 16 майнеров с шифровальщиками написали и гг гарене
Мемхак = максимальный функционал, и я считаю дурным тоном это не использование этого функционала, а эту способность в примере можно и на гуи сделать, чтобы визуально было так же, зачем вообще делать то, что уже было сделано, когда можно просто заняться новым, даже в варкрафте
Работа над ошибками