Система столкновений: При столкновении 2ух способностей, выживает тот, у которого больше оставшегося урона. И его урон уменьшается на урон проигравшего и тоже останавливается, если ост. урон у него < минимального. Смысл в возможности остановить сильную способность несколькими слабыми. Или ослабить принимаемый урон своим, менее сильным скиллом. Также есть опция, включающая столкновения с юнитами для скилла по тем же правилам. Конструктор скиллов: Очень гибкий конструктор скиллов с "летящим зарядом". Особо актуально для аниме карт. Инструкция по использованию: достаточно строчки
Код
call SW_AddSpell(int abilId, int dummy, real spd, real r, real dmg1, real dmg2, real dist, real startDist, bool stoppable, string startFunc, string loopFunc, string attackFunc, string endFunc)
Для добавления скилла. По пунктам:
abilId - абилка для каста
dummy - тип дамми юнита
spd - "шаг" снаряда
r - радиус для столкновений и нанесения урона
dmg1,dmg2 - урон считается как: dmg1+dmg2*(лвл скилла)
dist - дистанция, которую пролетит снаряд
startDist - дистанция появления дамми от кастера
stoppable - будет ли скилл сталкиваться с юнитами, или же будет просто наносить урон
startFunc - действия в начале скилла, сразу после создания дамми
loopFunc - действия после каждого шага
attackFunc - действия сразу после нанесения урона. Доступна функция GetAttackedUnit()
endFunc - действия перед окончанием скилла, прежде чем будет удален дамми
Команды пользователя:
Для start,loop,attack,delFunc
Код
SW_Set(x)/Get(): Speed - real //переводится в SpdX и SpdY Angle - real SpdX - real //Лучше, чем Speed и Angle SpdY - real R - real //радиус Dist - integer //!!оставшееся количество шагов!! Hp - real //оставшийся урон также: SW_GetDummy() SW_GetCaster() //вызвавший скилл. Доступно в любом месте SW_GetAttackedUnit() //только для attackFunc
Для эмуляции скилла без применения
Код
SW_EmulSpell(int abilId, unit caster, int level, real tarX, real tarY)
Таким образом, вы можете в принципе не делать скилла в РО, а записать как id любое слово из 4ех(к примеру 'FIRE') или число. Или же можно сделать свой обработчик каста(если до появления заряда вам надо сделать какие-то действия).
для добавления скилла
Код
SW_AddSpell( подробнее выше )
настройки
Код
SW_MIN_HP= //минимальный урон от скилла. SW_TIMEOUT= //период главного таймера. По умолчанию 0.03. обновляется при исчезновении всех дамми на карте
Особенности:
Поддерживает дамми со способностью "москиты","неуязвимый" и т.д.
Сама система столкновений.
Легкое в использовании
Не требует глубоких познаний в Jass для использования
Когда не используется, не грузит процессор.
Практически не ограничивает возможности скилла. В крайнем случае "на костылях" в юзерфунках можно всего добиться, а со знаниями cJass и изменить необходимое в коде.
Траектория полета снаряда, вопреки первому впечатлению, абсолютно произвольна, можно даже обнулить скорость и в loopFunc двигать снаряд за юнитом, сделав таким образом "пробивные" сталкивающиеся рассенганы(для Нарутофагов)
Умеренная нагрузка на комп, при сложности алгоритма (n+1)*n/2 (надеюсь, на этот раз угадал ) у меня тянет до 70 одновременно запущенных скиллов. Скриншоты:
Скачать: В карте 2 способности, реализованных на ГУИ и Jass2. Особо заморачиваться не стал, просто сделал способности, позволяющие в полной мере потестить систему столкновений(огнемет и местную Shock Wave). Ну и сама библиотека в нестандартном коде канешн. скачать карту-тест скачать *J Требуется jngp со включенным cJass Версия: Назовем ее 0.1 beta из-за одного бага, который я так и не смог победить. То, что я о нем знаю:
возникает от долгого применения огнемета.. Наверное.
проявляется в хаосе в индексации дамми в массиве. А последствия сами гляньте.
мгновенно показывает себя при использовании огнемета синим и красным игроками, но не появляется при использовании 2умя красными А еще забыл добавить callFunc, сильно ограничивает функционал. На днях исправлю. Код:
private real Dist(real x, real y, real x1, real y1){ return Sqrt(Sqr(x-x1)+Sqr(y-y1)) }
private real VLen(real x, real y){ return Sqrt(Sqr(x)+Sqr(y)) }
private real Min(real x, real y){ if(x<y){return x}else{return y} }
private real Abs(real x){ if(x<0){return -x}else{return x} }
//ОБЬЯВЛЕНИЕ private struct spell{ int abilId,dummy real spd,r,dmg1,dmg2,dist,startDist string startFunc,loopFunc,attackFunc,endFunc bool stoppable }
private struct shell{ real SpdX,SpdY,R,Hp unit u, caster//для пользователя int n, abilNum, Dist player owner real speed(){ return Sqrt(Sqr(this.SpdX)+Sqr(this.SpdY)) } bool stoppable }
globals private group ForLoop=CreateGroup() private spell Spell[100] private int SpellsNum=0 private shell Dummy[300] private int DummyNum=0 //Использует только для крипления структуры к скиллу private hashtable Hash=InitHashtable() private trigger CastTrg=CreateTrigger() private timer Timer=CreateTimer() //Номер дамми для передачи в польз. функции private int LastDummy //Последний атакованный юнит private unit AttackedUnit //НАСТРОЙКИ public real MIN_HP=4 public real TIMEOUT=0.03 endglobals //============================================================== //ПОЛЬЗОВАТЕЛЬСКИЕ ФУНКЦИИ public real GetSpeed(){ return Sqrt(Sqr(LD.SpdX)+Sqr(LD.SpdY)) }
public real GetAngle(){ return Atan2(LD.SpdY,LD.SpdX) }
public void SetSpeed(real spd){ real ang=GetAngle() LD.SpdX=spd*Cos(ang) LD.SpdY=spd*Sin(ang) }
public void SetAngle(real ang){ real spd=GetSpeed() LD.SpdX=spd*Cos(ang) LD.SpdY=spd*Sin(ang) }
public unit GetDummy(){ return LD.u }
public unit GetCaster(){ return LD.caster }
public void SetDist(int x){ LD.Dist=x }
public int GetDist(){ return LD.Dist }
public unit GetAttackedUnit(){ return AttackedUnit }
//создание остальных Get и Set функций define private ADD(X) = { public real Get##X(){ return Dummy[LastDummy].X } public void Set##X(real x){ Dummy[LastDummy].X = x } } ADD(SpdX) ADD(SpdY) ADD(R) ADD(Hp)
public void AddSpell(int abilId, int dummy, real spd, real r, real dmg1, real dmg2, real dist, real startDist, bool stoppable, string startFunc, string loopFunc, string attackFunc, string endFunc){ SpellsNum++ Spell[SpellsNum]=spell.create() setdef SET(x) = Spell[SpellsNum].x=x SET(abilId) SET(dummy) SET(spd) SET(r) SET(dmg1) SET(dmg2) SET(dist) SET(startDist) SET(stoppable) SET(startFunc) SET(attackFunc) SET(loopFunc) SET(endFunc) SaveInteger(Hash,abilId,0,SpellsNum) } //СИСТЕМА
private void Loop(){ define{ private D1 = Dummy[i] private D2 = Dummy[j] private end = { ExecuteFunc(Spell[Dummy[i].abilNum].endFunc) DelDummy(i) i-- } } for(int i=0;i<DummyNum;i++){ bool ok=true real min LastDummy=i D1.Dist-- SetUnitPosition(D1.u,GetX(D1.u)+D1.SpdX,GetY(D1.u)+D1.SpdY) if(i!=DummyNum-1){ for(int j=i+1;j<DummyNum;j++){ if((IsPlayerEnemy(D2.owner,D1.owner))&&(Dist(GetX(D1.u),GetY(D1.u),GetX(D2.u),GetY(D2.u))<=D1.R+D2.R+D2.speed())){ min=Min(D1.Hp,D2.Hp) D1.Hp-=min D2.Hp-=min if(D2.Hp<MIN_HP){DelDummy(j)} //конец скилла при столкновении if(D1.Hp<MIN_HP){ end ok=false break } } } } if(ok){ ExecuteFunc(Spell[D1.abilNum].loopFunc) GroupEnumUnitsInRange(ForLoop,GetX(D1.u),GetY(D1.u),D1.R,null) unit u loop u=FirstOfGroup(ForLoop) exitwhen u==null if IsUnitEnemy(u,D1.owner){ if(D1.stoppable){ min=Min(D1.Hp,GetHp(u)) D1.Hp-=min }else{min=D1.Hp} UnitDamageTarget(D1.u,u,min,false,false,ATTACK_TYPE_MAGIC,DAMAGE_TYPE_MAGIC,WEAPON_TYPE_WHOKNOWS) AttackedUnit=u ExecuteFunc(Spell[D1.abilNum].attackFunc) if(D1.Hp==0){ //конец скилла при попадении end ok=false GroupClear(ForLoop) break } } GroupRemoveUnit(ForLoop,u) endloop //конец скилла по расстоянию if((ok)&&(D1.Dist<1)){end} } } if(DummyNum==0){ PauseTimer(Timer) } //BJDebugMsg(I2S(DummyNum)) }
private void AddDummy(int i, unit u, int lvl, real tarX, real tarY){ Dummy[DummyNum]=shell.create() define{ private sp = Spell[i] private D = Dummy[DummyNum] } real x=GetUnitX(u), y=GetUnitY(u); real ang=AngBetP(x,y,tarX,tarY) D.stoppable=sp.stoppable D.owner=GetOwningPlayer(u) D.abilNum=i D.caster=u D.u=CreateUnit(D.owner,sp.dummy,x+(Cos(ang)*sp.startDist),y+(Sin(ang)*sp.startDist),ang*bj_RADTODEG) D.SpdX=sp.spd*Cos(ang) D.SpdY=sp.spd*Sin(ang) D.R=sp.r D.Hp=sp.dmg1+sp.dmg2*lvl// D.Dist=R2I(sp.dist/D.speed()) Dummy[DummyNum].n=DummyNum if(DummyNum==0){ TimerStart(Timer,TIMEOUT,true,function Loop) } LastDummy=DummyNum ExecuteFunc(sp.startFunc) u=null DummyNum++ }
private void Cast(){ int i=LoadInteger(Hash,GetSpellAbilityId(),0) if(i!=0){ AddDummy(i,GetTriggerUnit(),GetUnitAbilityLevel(GetTriggerUnit(),GetSpellAbilityId()),GetSpellTargetX(),GetSpellTargetY()) } }
public void EmulSpell(int abilId, unit caster, int level, real tarX, real tarY){ int i=LoadInteger(Hash,abilId,0) if(i!=0){ AddDummy(i,caster, level, tarX,tarY) } }
Эмм. а зачем юзать расстояние каждые 0.03 секунды, если можно вычислить время полета? Код тяжело читается и мне не понравилось вот что: Постоянное сообщение: Расходы.... При смерти даммика, не показывается эффект SetUnitPosition можно использовать SetUnitX SetUnitY много дефайнов, что понижает читабельность числа real заменяешь на int, что отнимает производительность. Поставь просто точки в конце реалов Кстати...native SquareRoot takes real x returns real Ах да, зачем юзать две структуры?
Код
public unit GetDummy(){ return LD.u }
public unit GetCaster(){ return LD.caster }
public void SetDist(int x){ LD.Dist=x }
public int GetDist(){ return LD.Dist }
public unit GetAttackedUnit(){ return AttackedUnit }
Можно заменить на дефайны, не? Но так как система только вышла на свет, ждемс исправлений. Интересная система выйдет
Расходы-это я для теста поставил даммикам пищу 1. Сто бы видеть кол-во. А все "конечные" эффекты в endFunc, согласись, там Removeunit() уместнее KillUnit().
Цитата
Можно заменить на дефайны, не?
Ага, надо бы. А две структуры для разных целей: Одна-скиллы, статичные данные для передачи заряду при старте. Другая-заряд, динамичные данные, содержащие информацию о конкретном запущенном заряде. За советы спасибо . Еще бы баг мне кто отловить помог
Goodie, там в коменте самом нижнем в триггерах написано, как его быстро вызвать. Либо тупо с огнеметом минут 5 бегать. Ты предлагаешь spell структуру с массивом shell структур, или просто с параллельными массивами? Ну да. Я чет не догадался
Ну. Но принцип стека "последним пришел-первым ушел" тут бесполезен. В нашем же случае "входы" и "выходы" даже в пределах одного скилла абсолютно хаотичны. Тут я самую быструю структуру использовал с незначительным по современным меркам ущербом в памяти
А вот и нифига, там второй цикл от i+1 идет => у нас будет ар. прогрессия с 1ым членом n и разностью (-1) (n+(n-1)+(n-2)...+1), что понятным образом преобразуется в n(n-1)/2
Добавлено (28 Июня 2013, 10:39:17) ---------------------------------------------
Цитата (SirNikolas)
Может, здесь уместнее был бы callback onUnitSpellCast или ...SpellEffect?
Добавлено (28 Июня 2013, 11:03:00) --------------------------------------------- Ty3uK, SirNikolas, ну там же надо spellId указывать. А мне надо обрабатывать все касты
xomach, делать все до минимальных утечек и прочей фигни, типа как переводить числа в HEX и прочее. НАсчет HEX эт можно, ну если привык и раньше писал, а вот насчет того чтобы убрать прям всё чтобы было нереально идеально, бред Чем больше ты пытаешься оптимизировать тем хуже ты делаешь
Просто меня убивает написание кода:
Написав это:
Код
callback onUnitSpellEffect('A000') { BJDebugMsg("Hello!") s }
Там мы видим это...:
Код
function cjCallbacksRegestration__reg_0b takes nothing returns nothing local integer i=GetSpellAbilityId() if i==0x41303030 then call cj_callback_00000000() endif endfunction
function cjCallbacksRegestration__initX takes nothing returns nothing local integer i=0 local player p loop set p=Player(i) call TriggerRegisterPlayerUnitEvent(cj_callback_trg_0b, p, EVENT_PLAYER_UNIT_SPELL_EFFECT, null) set i=i+1 exitwhen i==16 endloop call TriggerAddAction(cj_callback_trg_0b, function cjCallbacksRegestration__reg_0b) endfunction
здесь спелл расчитан на 17 игроков, что не надо и можно поставить на 15 Так же в функции указано действие, а не условие, a условие быстрее
Код
function cjCallbacksRegestration__reg_0b takes nothing returns nothing local integer i=GetSpellAbilityId() if i==0x41303030 then call cj_callback_00000000() endif endfunction
Зачем вызывать функцию если там можно внутри уже все написать или даже хотябы в вызове написать аргументы
Добавлено (28 Июня 2013, 11:17:18) ---------------------------------------------