Добавлен nazarpunk,
опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
Zinc
Тип:
Способность
Версия Warcraft:
1.26+
Plague Barrel
MUI: да
Импорт: иконка, снаряд
Утечки: нет
Требования: JNGP
Описание: Герой бросает бочку заразы, которая при взрыве наносит 100*уровень заклинания магического урона.
Импорт: иконка, снаряд
Утечки: нет
Требования: JNGP
Описание: Герой бросает бочку заразы, которая при взрыве наносит 100*уровень заклинания магического урона.
- тонет в воде
- сталкивается с другими бочками
- взаимодействует с рельефом
- юнит-снаряд принадлежит нейтрально пассивному и не портит статистику
- взрыв постепенно распространяется в 3D
- использует хэштаблицу для хранения id таймера
Скриншот
Технические подробности
Перенос в свою карту
Способности
- 'AUpb' Plague Barrel (герой)
Войска
- 'upbd' Plague Barrel (снаряд)
Триггеры
- SpellPlagueBarrel
Импорт
- Missile\PlagueBarrel.mdx
- ReplaceableTextures\CommandButtons\BTNPlagueBarrel.blp
- ReplaceableTextures\CommandButtonsDisabled\DISBTNPlagueBarrel.blp
Настройка
constant integer AbilityID = 'AUpb'; // Равкод способности
constant integer BarrelID = 'upbd'; // Равкод бочки
constant player BarrelOwner = Player(PLAYER_NEUTRAL_PASSIVE); // Владелец бочки
constant real BarrelTimerPeriod = 0.03125; // Период таймера бочки: 1/32 секунды
constant real BarrelSpeedSec = 1200; // Расстояние, которое бочка пройдёт за секунду
constant real BarrelSpeedAdd = BarrelSpeedSec/(1/BarrelTimerPeriod); // Расстояние, которое бочка пройдёт за каждый тик таймера
constant real BarrelArc = 0.30; // Изгиб параболы
constant real BarrelStartZ = 140; // Высота бочки в начале полёта
constant real BarrelStartD = 100; // Расстояние бочки от кастера в начале полёта
constant real BarrelCollisionRadius = 30; // Радиус бочки для расчёта столкновений
unit BarrelCollisionUnit; // Не трогать
group BarrelCollisionGroup = CreateGroup(); // Не трогать
constant real ExplodeTimerPeriod = 0.03125; // Период таймера распространения взрыва: 1/32 секунды
constant real ExplodeSpeedSec = 500; // Расстояние, на которое распространится взрыв за секунду
constant real ExplodeSpeedAdd = ExplodeSpeedSec/(1/ExplodeTimerPeriod); // Расстояние, на которое взрыв распространится за каждый тик таймера
hashtable HT = InitHashtable(); // Хэштаблица для таймера
// Можете вписать туда вашу таблицу, например:
// hashtable HT = udg_HashTable;
location locationZ = Location(0, 0); // Не трогать
// Радиус захвата цели при взрыве
function getExplodeRange(integer level) -> integer {
return 350;
}
// Проверка целей
function checkTarget(unit caster, unit target) -> boolean {
return (
caster != target // Не кастер
&&
!IsUnitType(target, UNIT_TYPE_STRUCTURE) // Не здание
&&
!IsUnitType(target, UNIT_TYPE_MECHANICAL) // Не механический
&&
!IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) // Восприимчив к магии
&&
IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(target)) // Враг
);
}
// Функция, вызываемая при нанесении урона
function onDamage(unit caster, unit target, integer level){
real damage = level*100;
UnitDamageTarget(caster, target, damage, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
}
// Функция, вызываемая при взрыве бочки
function onExplode(unit caster, integer level, real x, real y, real z){
// caster - кто призвал бочку
// level - уровень способности
// x, y, z - координаты бочки
}
// Функкция, вызываемая при попадании бочки в глубокую воду
function onDeepWater(unit caster, integer level, real x, real y, real z){
DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl", x, y));
}
Код заклинания
//! zinc
library SpellPlagueBarrel {
constant integer AbilityID = 'AUpb'; // Равкод способности
constant integer BarrelID = 'upbd'; // Равкод бочки
constant player BarrelOwner = Player(PLAYER_NEUTRAL_PASSIVE); // Владелец бочки
constant real BarrelTimerPeriod = 0.03125; // Период таймера бочки: 1/32 секунды
constant real BarrelSpeedSec = 1200; // Расстояние, которое бочка пройдёт за секунду
constant real BarrelSpeedAdd = BarrelSpeedSec/(1/BarrelTimerPeriod); // Расстояние, которое бочка пройдёт за каждый тик таймера
constant real BarrelArc = 0.30; // Изгиб параболы
constant real BarrelStartZ = 140; // Высота бочки в начале полёта
constant real BarrelStartD = 100; // Расстояние бочки от кастера в начале полёта
constant real BarrelCollisionRadius = 30; // Радиус бочки для расчёта столкновений
unit BarrelCollisionUnit; // Не трогать
group BarrelCollisionGroup = CreateGroup(); // Не трогать
constant real ExplodeTimerPeriod = 0.03125; // Период таймера распространения взрыва: 1/32 секунды
constant real ExplodeSpeedSec = 500; // Расстояние, на которое распространится взрыв за секунду
constant real ExplodeSpeedAdd = ExplodeSpeedSec/(1/ExplodeTimerPeriod); // Расстояние, на которое взрыв распространится за каждый тик таймера
hashtable HT = InitHashtable(); // Хэштаблица для таймера
// Можете вписать туда вашу таблицу, например:
// hashtable HT = udg_HashTable;
location locationZ = Location(0, 0); // Не трогать
// Радиус захвата цели при взрыве
function getExplodeRange(integer level) -> integer {
return 350;
}
// Проверка целей
function checkTarget(unit caster, unit target) -> boolean {
return (
caster != target // Не кастер
&&
!IsUnitType(target, UNIT_TYPE_STRUCTURE) // Не здание
&&
!IsUnitType(target, UNIT_TYPE_MECHANICAL) // Не механический
&&
!IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) // Восприимчив к магии
&&
IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(target)) // Враг
);
}
// Функция, вызываемая при нанесении урона
function onDamage(unit caster, unit target, integer level){
real damage = level*100;
UnitDamageTarget(caster, target, damage, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
}
// Функция, вызываемая при взрыве бочки
function onExplode(unit caster, integer level, real x, real y, real z){
// caster - кто призвал бочку
// level - уровень способности
// x, y, z - координаты бочки
}
// Функкция, вызываемая при попадании бочки в глубокую воду
function onDeepWater(unit caster, integer level, real x, real y, real z){
DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl", x, y));
}
//
// Заклинание
//
// Polar: https://xgm.guru/p/wc3/polar
function GetTerrainZ(real x, real y) -> real {
MoveLocation(locationZ, x, y);
return GetLocationZ(locationZ);
}
function GetUnitZ(unit target) -> real {
return GetTerrainZ(GetUnitX(target), GetUnitY(target)) + GetUnitFlyHeight(target);
}
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 DistanceBetweenCoords3D(real x1, real y1, real z1, real x2, real y2, real z2) -> real {
real dx = x2 - x1;
real dy = y2 - y1;
real dz = z2 - z1;
return SquareRoot(dx*dx + dy*dy + dz*dz);
}
function AngleBetweenCoords(real x1, real y1, real x2, real y2) -> real {
return bj_RADTODEG * Atan2(y2 - y1, x2 - x1);
}
function SetUnitZ(unit target, real z) -> unit {
SetUnitFlyHeight(target, z - GetTerrainZ(GetUnitX(target), GetUnitY(target)), 0);
return target;
}
function ParabolaZ2(real zs, real ze, real h, real d, real x) -> real {
return (2*(zs + ze - 2*h)*(x/d - 1) + (ze - zs))*(x/d) + zs;
}
// endPolar
function isUnitAlive(unit target) -> boolean {
return GetWidgetLife(target) > 0.405;
}
function collision(){
unit u1 = GetEnumUnit();
unit u2 = BarrelCollisionUnit;
real d = DistanceBetweenCoords3D(GetUnitX(u1), GetUnitY(u1), GetUnitZ(u1), GetUnitX(u2), GetUnitY(u2), GetUnitZ(u2));
if (u1 != u2 && d <= BarrelCollisionRadius){
KillUnit(u1);
KillUnit(u2);
}
u1 = null;
u2 = null;
}
function isDeepWater(real x, real y) -> boolean {
return (!IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) && IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY));
}
// explode
struct explode {
unit caster;
group targets;
integer level;
real x, y, z, range, lim;
static method create(unit caster, integer level, real x, real y, real z) -> explode {
explode this = explode.allocate();
timer t = CreateTimer();
this.caster = caster;
this.targets = CreateGroup();
this.range = ExplodeSpeedAdd;
this.lim = getExplodeRange(level);
this.level = level;
this.x = x;
this.y = y;
this.z = z;
SaveInteger(HT, GetHandleId(t), 0, this);
TimerStart(t, ExplodeTimerPeriod, true, function explode.callback);
t = null;
return this;
}
method destroy(){
this.caster = null;
GroupClear(this.targets);
DestroyGroup(this.targets);
this.targets = null;
this.deallocate();
}
static method callback(){
timer t = GetExpiredTimer();
integer tid = GetHandleId(t);
explode this = LoadInteger(HT, tid, 0);
group g = CreateGroup();
unit u;
this.range = this.range + ExplodeSpeedAdd;
if (this.range > this.lim){
this.destroy();
PauseTimer(t);
DestroyTimer(t);
FlushChildHashtable(HT, tid);
} else {
GroupEnumUnitsInRange(g, this.x, this.y, this.range, Filter(function() -> boolean {
return isUnitAlive(GetFilterUnit());
}));
while(true){
u = FirstOfGroup(g);
if (u == null) { break; }
if (
!IsUnitInGroup(u, targets)
&&
checkTarget(this.caster, u)
&&
DistanceBetweenCoords3D(this.x, this.y, this.z, GetUnitX(u), GetUnitY(u), GetUnitZ(u)) <= range
){
GroupAddUnit(this.targets, u);
onDamage(this.caster, u, this.level);
}
GroupRemoveUnit(g, u);
}
}
DestroyGroup(g); g = null;
u = null;
t = null;
}
}
// barrel
struct barrel {
unit caster, barrel;
real x, y, z, zs, ze, h, a, d, l, sin, cos;
integer level, barrelID;
static method create(unit caster, real x, real y) -> barrel {
barrel this = barrel.allocate();
timer t = CreateTimer();
this.caster = caster;
this.level = GetUnitAbilityLevel(caster, AbilityID);
this.x = GetUnitX(caster);
this.y = GetUnitY(caster);
this.z = GetUnitZ(caster) + BarrelStartZ;
this.zs = this.z;
this.ze = GetTerrainZ(x, y);
this.a = AngleBetweenCoords(this.x, this.y, x, y);
this.cos = Cos(a * bj_DEGTORAD);
this.sin = Sin(a * bj_DEGTORAD);
this.x = this.x + BarrelStartD * this.cos;
this.y = this.y + BarrelStartD * this.sin;
this.d = DistanceBetweenCoords(this.x, this.y, x, y);
this.h = this.d*BarrelArc + RMaxBJ(this.ze, this.zs);
this.l = 0;
this.barrel = CreateUnit(BarrelOwner, BarrelID, this.x, this.y, this.a);
SetUnitX(this.barrel, this.x);
SetUnitY(this.barrel, this.y);
SetUnitZ(this.barrel, this.z);
GroupAddUnit(BarrelCollisionGroup, this.barrel);
this.barrelID = GetHandleId(this.barrel);
SaveUnitHandle(HT, this.barrelID, 0, this.caster);
SaveInteger(HT, this.barrelID, 0, this.level);
SaveInteger(HT, GetHandleId(t), 0, this);
TimerStart(t, BarrelTimerPeriod, true, function barrel.callback);
t = null;
return this;
}
method destroy(){
this.caster = null;
this.barrel = null;
this.deallocate();
}
static method callback(){
timer t = GetExpiredTimer();
integer tid = GetHandleId(t);
barrel this = LoadInteger(HT, tid, 0);
boolean isDestroy = false;
if (isUnitAlive(this.barrel)){
this.l = this.l + BarrelSpeedAdd;
this.x = this.x + BarrelSpeedAdd * this.cos;
this.y = this.y + BarrelSpeedAdd * this.sin;
this.z = ParabolaZ2(this.zs, this.ze, this.h, this.d, this.l);
if (this.z > GetTerrainZ(this.x, this.y) + BarrelCollisionRadius*2){
SetUnitX(this.barrel, this.x);
SetUnitY(this.barrel, this.y);
SetUnitZ(this.barrel, this.z);
BarrelCollisionUnit = this.barrel;
ForGroup(BarrelCollisionGroup, function collision);
} else {
if (IsUnitInGroup(this.barrel, BarrelCollisionGroup)){
GroupRemoveUnit(BarrelCollisionGroup, this.barrel);
}
if (isDeepWater(this.x, this.y)){
FlushChildHashtable(HT, GetHandleId(this.barrel));
onDeepWater(this.caster, this.level, this.x, this.y, this.z);
RemoveUnit(this.barrel);
} else {
KillUnit(this.barrel);
}
isDestroy = true;
}
} else {
isDestroy = true;
}
if (isDestroy){
this.destroy();
PauseTimer(t);
DestroyTimer(t);
FlushChildHashtable(HT, tid);
}
t = null;
}
}
function onInit(){
integer i;
trigger t;
t = CreateTrigger();
for (0 <= i < bj_MAX_PLAYER_SLOTS){
TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null);
}
TriggerAddCondition(t, Filter(function() -> boolean {
if (GetSpellAbilityId() == AbilityID){
barrel.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY());
}
return false;
}));
t = CreateTrigger();
TriggerRegisterPlayerUnitEvent(t, BarrelOwner, EVENT_PLAYER_UNIT_DEATH, null);
TriggerAddCondition(t, Filter(function() -> boolean {
return GetUnitTypeId(GetTriggerUnit()) == BarrelID;
}));
TriggerAddAction(t, function(){
unit u = GetTriggerUnit();
integer uid = GetHandleId(u);
GroupRemoveUnit(BarrelCollisionGroup, u);
explode.create(
LoadUnitHandle(HT, uid, 0),
LoadInteger(HT, uid, 0),
GetUnitX(u),
GetUnitY(u),
GetUnitZ(u)
);
FlushChildHashtable(HT, uid);
u = null;
});
t = null;
}
}
//! endzinc
Благодарности
denismilyaev1 за предоставленную модельку.
`
ОЖИДАНИЕ РЕКЛАМЫ...
Комментарии пока отсутcтвуют.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.