Добавлен ScorpioT1000,
опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
vJass
Тип:
Наработка
Ничего необычного, но раз требуется - почему бы и не сделать.
Наработка позволяет делать перебор всех юнитов в заданной области от центральных к крайним и наоборот.
Возможности:
Возможности:
- собственно, перебирать юнитов, для чего используются операторы квадратных скобок [], как у простого массива;
- каждый новый ScSortedSelector полностью независим от остальных;
- сортировка_выбором - это реально быстрее, чем производить GroupEnumUnitsInRange рекурсивно;
- полное 3D пространство, сортировка происходит по отдаленности юнитов в сфере от изначальной позиции;
- оперирование от произвольной 2D или 3D области (без кастера);
- оперирование от лица кастера;
- оперирование от лица кастера, у которого способность с целью;
- поддержка фильтров и доступ из фильтра к основным данным кастера;
- возможность "мягко" ограничить макс. число юнитов на каждый отдельный каст (например, для уровней);
- нормально документирован;
- есть пара примеров с молниями swdn и просто изменением цвета;
- богатый набор стандартных фильтров;
Интерфейс управления:
// ScSortedSelector
// by ScorpioT1000, 2013
library ScSortedSelector
{
// Available standard filters // Доступные стандартные фильтры
boolexpr SC_FILTER_ANY;
boolexpr SC_FILTER_ANY_NOT_CASTER;
boolexpr SC_FILTER_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ALLY_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ALLY_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ENEMY_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD;
boolexpr SC_FILTER_ALLY_NOT_DEAD;
boolexpr SC_FILTER_DEAD_NOT_CASTER;
// Returns current filter ScSelector caster. Works only with NewFromUnit method!
// Возвращает заклинателя текущего фильтра ScSelector. Работает только с методом NewFromUnit!
#define ScGetFilterCaster() = ScSelector.getfiltercaster
// Returns current filter ScSelector start coords // Возвращает начальные координаты текущего фильтра ScSelector
#define ScGetFilterX() = ScSelector.getfilterx
#define ScGetFilterY() = ScSelector.getfiltery
#define ScGetFilterZ() = ScSelector.getfilterz
// Returns current filter ScSelector target; already defined in Common.j // Возвращает цель текущего фильтра ScSelector
// GetEnumUnit = GetEnumUnit
struct ScSelector
{
// Creates a new ScSelector from the specified unit coords (for no-target spells)
// Создает новый ScSelector из указанных координат юнита (для способностей без цели)
static thistype NewFromUnit(unit caster, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(GetUnitX(caster),GetUnitY(caster),GetUnitZ(caster),radius,filter,caster,reverseSort,maxUnitsAffected);
}
// Creates a new ScSelector in 3D from the specified custom coords and caster unit (for targeted spells)
// Создает новый ScSelector в 3D из указанных нестандартных координат и юнита-заклинателя (для способностей с целью)
static thistype NewFromTarget(unit caster, \
real x, \
real y, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(x,y,thistype.GetZ(x,y),radius,filter,caster,reverseSort,maxUnitsAffected);
}
// Creates a new ScSelector in 2D, doesn't match z coord this time
// Создает новый ScSelector в 2D, не учитывает z координату на этот раз
static thistype New(real x, \
real y, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(x,y,-110000,radius,filter,null,reverseSort,maxUnitsAffected);
}
// Creates a new ScSelector in 3D // Создает новый ScSelector в 3D
static thistype New3D(real x, \
real y, \
real z, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(x,y,z,radius,filter,null,reverseSort,maxUnitsAffected);
}
// Deletes existing ScSelector
void Delete()
{
.Clear();
.destroy();
}
// You can access units using [] operator
unit operator[](int index) {
if(index > .c) { return null; }
return .u[index];
}
// You can change units using []= operator
void operator[]=(int index, unit value) {
if(index > .c) { return; }
.u[index] = value;
}
// Returns size of array
int Size() { return .c; }
// void Sort(bool reverse);
// void Select(boolexpr filter);
// void Clear();
// void Print();
Full Source
#include "cj_types.j"
// ScSortedSelector
// by ScorpioT1000, 2013
library ScSortedSelector
{
// Available standard filters // Доступные стандартные фильтры
boolexpr SC_FILTER_ANY;
boolexpr SC_FILTER_ANY_NOT_CASTER;
boolexpr SC_FILTER_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ALLY_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ALLY_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_ENEMY_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD_NOT_CASTER;
boolexpr SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD;
boolexpr SC_FILTER_ALLY_NOT_DEAD;
boolexpr SC_FILTER_DEAD_NOT_CASTER;
// Returns current filter ScSelector caster. Works only with NewFromUnit method!
// Возвращает заклинателя текущего фильтра ScSelector. Работает только с методом NewFromUnit!
#define ScGetFilterCaster() = ScSelector.getfiltercaster
// Returns current filter ScSelector start coords // Возвращает начальные координаты текущего фильтра ScSelector
#define ScGetFilterX() = ScSelector.getfilterx
#define ScGetFilterY() = ScSelector.getfiltery
#define ScGetFilterZ() = ScSelector.getfilterz
// Returns current filter ScSelector target; already defined in Common.j // Возвращает цель текущего фильтра ScSelector
// GetEnumUnit = GetEnumUnit
struct ScSelector
{
// Creates a new ScSelector from the specified unit coords (for no-target spells)
// Создает новый ScSelector из указанных координат юнита (для способностей без цели)
static thistype NewFromUnit(unit caster, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(GetUnitX(caster),GetUnitY(caster),GetUnitZ(caster),radius,filter,caster,reverseSort,maxUnitsAffected);
}
// Creates a new ScSelector in 3D from the specified custom coords and caster unit (for targeted spells)
// Создает новый ScSelector в 3D из указанных нестандартных координат и юнита-заклинателя (для способностей с целью)
static thistype NewFromTarget(unit caster, \
real x, \
real y, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(x,y,thistype.GetZ(x,y),radius,filter,caster,reverseSort,maxUnitsAffected);
}
// Creates a new ScSelector in 2D, doesn't match z coord this time
// Создает новый ScSelector в 2D, не учитывает z координату на этот раз
static thistype New(real x, \
real y, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(x,y,-110000,radius,filter,null,reverseSort,maxUnitsAffected);
}
// Creates a new ScSelector in 3D // Создает новый ScSelector в 3D
static thistype New3D(real x, \
real y, \
real z, \
real radius, \ // = 500.0
boolexpr filter, \ // = SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER
bool reverseSort, \ // = false
int maxUnitsAffected) // = -1
{
return thistype.doCreate(x,y,z,radius,filter,null,reverseSort,maxUnitsAffected);
}
// Deletes existing ScSelector
void Delete()
{
.Clear();
.destroy();
}
// You can access units using [] operator
unit operator[](int index) {
if(index > .c) { return null; }
return .u[index];
}
// You can change units using []= operator
void operator[]=(int index, unit value) {
if(index > .c) { return; }
.u[index] = value;
}
// Returns size of array
int Size() { return .c; }
// void Sort(bool reverse);
// void Select(boolexpr filter);
// void Clear();
// void Print();
/** =============================================================== */
/** ======================= PRIVATE SECTION ======================= */
#define SC_SELECTOR_MAX_UNITS = 96
#define is3d = (.startZ < (-100000))
#define private swap(A,B,T) = { T T##tmp = A; A = B; B = T##tmp; }
private unit u[SC_SELECTOR_MAX_UNITS];
private real d[SC_SELECTOR_MAX_UNITS];
private int c;
private real startRadius;
private real startX;
private real startY;
private real startZ;
private unit me;
private int softLimit;
private static location GL = Location(0,0)
private static real GetUnitZ(unit u) {
MoveLocation(thistype.GL , GetUnitX(u) , GetUnitY(u));
return GetUnitFlyHeight(u) + GetLocationZ(thistype.GL);
}
private static real GetZ(real x, real y) {
MoveLocation(thistype.GL , x , y);
return GetLocationZ(thistype.GL);
}
private static thistype doCreate(real x, real y, real z, real radius, boolexpr filter, unit caster, bool reverseSort, int maxUnitsAffected) {
thistype this = thistype.allocate();
.c = 0;
.startRadius = radius;
.startX = x;
.startY = y;
.startZ = z;
if(maxUnitsAffected <= 0) { maxUnitsAffected = SC_SELECTOR_MAX_UNITS; }
.softLimit = maxUnitsAffected;
.me = caster;
.Select(filter);
.Sort(reverseSort);
return this;
}
void Print() {
int i=0;
BJDebugMsg("ScSelector["+I2S(this)+"] = { ("+I2S(c)+")," \
+R2SW(.startRadius,0,2)+"," \
+R2SW(.startX,0,2)+"," \
+R2SW(.startY,0,2)+"," \
+R2SW(.startZ,0,2));
whilenot(i >= c) {
BJDebugMsg(" "+R2SW(GetUnitX(u[i]),0,2)+"," \
+R2SW(GetUnitY(u[i]),0,2)+"," \
+R2SW(thistype.GetUnitZ(u[i]),0,2) \
+": "+R2SW(d[i],0,2)+"," \
+" id="+I2S(i)+",");
++i;
}
BJDebugMsg("}");
}
void Select(boolexpr filter) {
thistype.mgetfiltercaster = .me;
thistype.mgetfilterx = .startX;
thistype.mgetfiltery = .startY;
thistype.mgetfilterz = .startZ;
real x,y,z;
group g = CreateGroup();
.Clear();
GroupEnumUnitsInRange(g,.startX,.startY,.startRadius,filter);
unit w = FirstOfGroup(g);
whilenot((w == null) || (.c >= SC_SELECTOR_MAX_UNITS)) {
x = GetUnitX(w) - .startX;
y = GetUnitY(w) - .startY;
if(is3d) {
z = thistype.GetUnitZ(w) - .startZ;
.d[.c] = SquareRoot(x*x + y*y + z*z)
if(.d[.c] <= .startRadius) {
.u[.c] = w;
}
} else {
.d[.c] = SquareRoot(x*x + y*y)
.u[.c] = w;
}
.c++;
GroupRemoveUnit(g,w);
w = FirstOfGroup(g);
}
GroupClear(g);
DestroyGroup(g);
g = null;
w = null;
}
// Selection sorting
void Sort(bool reverse) {
int i,j,iMin;
for (j = 0; j < (.c-1); ++j) {
iMin = j;
for ( i = j+1; i < .c; ++i) {
if(reverse) {
if (.d[i] > .d[iMin]) {
iMin = i;
}
} else {
if (.d[i] < .d[iMin]) {
iMin = i;
}
}
}
if ( iMin != j ) {
swap(.d[j], .d[iMin], real);
swap(.u[j], .u[iMin], unit);
}
}
}
void Clear() {
int i=0;
whilenot(i >= c) {
u[i] = null;
++i;
}
}
static unit mgetfiltercaster = null;
static real mgetfilterx = 0;
static real mgetfiltery = 0;
static real mgetfilterz = 0;
static unit getfiltercaster() { return mgetfiltercaster; }
static real getfilterx() { return mgetfilterx; }
static real getfiltery() { return mgetfilterx; }
static real getfilterz() { return mgetfilterx; }
}
#define private NOT_CASTER = { (ScGetFilterCaster() != GetEnumUnit()) }
#define private NOT_DEAD = { ((GetUnitTypeId( GetFilterUnit()) > 0) && !IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD)) }
#define private DEAD = { ((GetUnitTypeId( GetFilterUnit()) > 0) && IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD)) }
#define private ENEMY = { IsPlayerEnemy(GetOwningPlayer(ScGetFilterCaster()),GetOwningPlayer(GetFilterUnit())) }
#define private ALLY = { IsPlayerAlly(GetOwningPlayer(ScGetFilterCaster()),GetOwningPlayer(GetFilterUnit())) }
#define private SAME_PLAYER = { (GetOwningPlayer(ScGetFilterCaster()) == GetOwningPlayer(GetFilterUnit())) }
bool ScFilterAny() { return true; }
bool ScFilterAnyNotMe() { return NOT_CASTER; }
bool ScFilterNotDeadNotCaster() { return NOT_DEAD && NOT_CASTER; }
bool ScFilterAllyNotDeadNotCaster() { return ALLY && NOT_DEAD && NOT_CASTER; }
bool ScFilterAllyDeadNotCaster() { return ALLY && DEAD && NOT_CASTER; }
bool ScFilterEnemyNotDeadNotCaster() { return ENEMY && NOT_DEAD && NOT_CASTER; }
bool ScFilterEnemyDeadNotCaster() { return ENEMY && DEAD && NOT_CASTER; }
bool ScFilterSamePlayerNotDeadNotCaster() { return SAME_PLAYER && NOT_DEAD && NOT_CASTER; }
bool ScFilterSamePlayerDeadNotCaster() { return SAME_PLAYER && DEAD && NOT_CASTER; }
bool ScFilterSamePlayerOrAllyNotDeadNotCaster() { return (SAME_PLAYER || ALLY) && NOT_DEAD && NOT_CASTER; }
bool ScFilterSamePlayerOrAllyNotDead() { return (SAME_PLAYER || ALLY) && NOT_DEAD; }
bool ScFilterAllyNotDead() { return ALLY && NOT_DEAD; }
bool ScFilterDeadNotCaster() { return DEAD && NOT_CASTER; }
callback onInit() {
SC_FILTER_ANY = Condition(function ScFilterAny)
SC_FILTER_ANY_NOT_CASTER = Condition(function ScFilterAnyNotMe)
SC_FILTER_NOT_DEAD_NOT_CASTER = Condition(function ScFilterNotDeadNotCaster)
SC_FILTER_ALLY_NOT_DEAD_NOT_CASTER = Condition(function ScFilterAllyNotDeadNotCaster)
SC_FILTER_ALLY_DEAD_NOT_CASTER = Condition(function ScFilterAllyDeadNotCaster)
SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER = Condition(function ScFilterEnemyNotDeadNotCaster)
SC_FILTER_ENEMY_DEAD_NOT_CASTER = Condition(function ScFilterEnemyDeadNotCaster)
SC_FILTER_SAMEPLAYER_NOT_DEAD_NOT_CASTER = Condition(function ScFilterSamePlayerNotDeadNotCaster)
SC_FILTER_SAMEPLAYER_DEAD_NOT_CASTER = Condition(function ScFilterSamePlayerDeadNotCaster)
SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD_NOT_CASTER = Condition(function ScFilterSamePlayerOrAllyNotDeadNotCaster)
SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD = Condition(function ScFilterSamePlayerOrAllyNotDead)
SC_FILTER_ALLY_NOT_DEAD = Condition(function ScFilterAllyNotDead)
SC_FILTER_DEAD_NOT_CASTER = Condition(function ScFilterDeadNotCaster)
}
}
// ScorpioT1000 (c) 2013
Как это выглядит
Пример 1:
if(GetSpellAbilityId() == 'A000') {
// Создадим новый селектор, зададим параметры. По умолчанию см. в заголовках функиций
ScSelector s = ScSelector.NewFromTarget(GetTriggerUnit(),GetLocationX(l), GetLocationY(l), 410, SC_FILTER_ENEMY_NOT_DEAD_NOT_CASTER, true, -1);
// Начнем цикл от 0 до размера массива селектора
for(int i = 0; i < s.Size(); ++i) {
// ваш код
// вы можете свободно обращаться скобками
unit u = s[i];
s[i] = u;
TriggerSleepAction(0.08);
}
s.Delete();
}
Пример с лечением:
if(GetSpellAbilityId() == 'A002') {
ScSelector s = ScSelector.NewFromTarget(GetTriggerUnit(),GetLocationX(l), GetLocationY(l), 300, SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD, false, -1);
for(int i = 0; i < s.Size(); ++i) {
XLS_CreateOnUnits(XLS_HEALING_WAVE_2,GetTriggerUnit(),s[i],1.9);
TriggerSleepAction(0.05);
}
s.Delete();
}
Конечно, это примеры на скорую руку, желательно обходиться без вейтов.
Что за фильтры
При создании фильтры контроллируют, какие единицы могут быть затронуты селектором.
Все фильтры перечислены в самом начале модуля ScSortedSelector. Расшифрую, что означает каждая приписка:
ANY - любой юнит; NOT - отрицание, т.е. следующее за ним слово исключается при выборе; OR - логическое "или", т.е. может быть выполнено что-то одно (то, что слева или то что справа); CASTER - тот, кто вызвал заклинание (работает только если юнит был передан); DEAD - мертвые, но еще не удаленные; ALLY - союзники заклинателя; ENEMY - противники заклинателя; SAMEPLAYER - юниты, принадлежащие тому же игроку;
Все фильтры перечислены в самом начале модуля ScSortedSelector. Расшифрую, что означает каждая приписка:
ANY - любой юнит; NOT - отрицание, т.е. следующее за ним слово исключается при выборе; OR - логическое "или", т.е. может быть выполнено что-то одно (то, что слева или то что справа); CASTER - тот, кто вызвал заклинание (работает только если юнит был передан); DEAD - мертвые, но еще не удаленные; ALLY - союзники заклинателя; ENEMY - противники заклинателя; SAMEPLAYER - юниты, принадлежащие тому же игроку;
Тем самым, например, фильтр SC_FILTER_SAMEPLAYER_OR_ALLY_NOT_DEAD_NOT_CASTER означает, что в выборку будут добавлены юниты самого игрока, юниты его союзников, при этом они все не могут быть мертвыми и в выборку не добавится сам заклинатель.
Могут быть фаталы
Из-за немного кривой либы молний у меня были фаталы, но я сменил период и все пофиксилось O_o
Ещё могут быть фаталы, если обратиться к юниту, которого нет. Например, если вы создали селектор без заклинателя, а фильтр выбрали с проверкой на союз.
Ещё могут быть фаталы, если обратиться к юниту, которого нет. Например, если вы создали селектор без заклинателя, а фильтр выбрали с проверкой на союз.
Где дамаг?
Эта библиотека выбора, т.е. ее главная задача - отобрать для вас юнитов в правильном порядке, а что с ними делать - решает уже модмейкер.
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Вероятно всего for не видит, как быть?
Отредактирован ScorpioT1000
Clamp, я имел ввиду холивары с бугерменами, но ладно.
youtube, я тестил на варе 1.21b и на 1.26
Думаю, может еще одну сделать, "вирусная логика", которая будет позволять простыми телодвижениями переключаться между юнитами по логике распространения вируса?
Просто расскажи как это работает и для какой задачи писалось. Если не лень.
Отредактирован PT153
PT153, ну так вот решил тогда, не вспомню уже, можно супер квик сорт уже юзать