| 01-06-2011, 07:09 AM | #1 |
After this spell is activated, the allies in front of you get X healing points per 2.5 sec.. This spell drains your mana continuously. requires TimerUtils TimerUtils:library TimerUtils globals private constant integer MAX_HANDLE_ID_COUNT = 408000 // values lower than 8191: very fast, but very unsafe. // values bigger than 8191: not that fast, the bigger the number is the slower the function gets // Most maps don't really need a value bigger than 50000 here, but if you are unsure, leave it // as the rather inflated value of 408000 private integer array data[MAX_HANDLE_ID_COUNT] private constant integer MIN_HANDLE_ID=0x100000 //use a totally random number here, the more improbable someone uses it, the better. private constant integer HELD=0x28829022 private timer array tT private integer tN = 0 endglobals //It is dependent on jasshelper's recent inlining optimization in order to perform correctly. function SetTimerData takes timer t, integer value returns nothing debug if(GetHandleId(t)-MIN_HANDLE_ID>=MAX_HANDLE_ID_COUNT) then debug call BJDebugMsg("SetTimerData: Handle id too big, increase the max handle id count or use gamecache instead") debug endif set data[GetHandleId(t)-MIN_HANDLE_ID]=value endfunction function GetTimerData takes timer t returns integer debug if(GetHandleId(t)-MIN_HANDLE_ID>=MAX_HANDLE_ID_COUNT) then debug call BJDebugMsg("GetTimerData: Handle id too big, increase the max handle id count or use gamecache instead") debug endif return data[GetHandleId(t)-MIN_HANDLE_ID] endfunction function NewTimer takes nothing returns timer if (tN==0) then set tT[0]=CreateTimer() else set tN=tN-1 endif call SetTimerData(tT[tN],0) return tT[tN] endfunction function ReleaseTimer takes timer t returns nothing if(t==null) then debug call BJDebugMsg("Warning: attempt to release a null timer") return endif if (tN==8191) then debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!") //stack is full, the map already has much more troubles than the chance of bug call DestroyTimer(t) else call PauseTimer(t) if(GetTimerData(t)==HELD) then debug call BJDebugMsg("Warning: ReleaseTimer: Double free!") return endif call SetTimerData(t,HELD) set tT[tN]=t set tN=tN+1 endif endfunction endlibrary GroupUtils GroupUtils:library GroupUtils initializer Init requires optional xebasic //****************************************************************************** //* BY: Rising_Dusk //* //* This library is a combination of several features relevant to groups. First //* and foremost, it contains a group stack that you can access dynamic groups //* from. It also provides means to refresh groups and clear any shadow //* references within them. The included boolexprs are there for backwards //* compatibility with maps that happen to use them. Since the 1.24c patch, //* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no //* performance gain to using the BOOLEXPR_TRUE constant. //* //* Instead of creating/destroying groups, we have moved on to recycling them. //* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always //* remember to call ReleaseGroup on a group when you are done using it. If you //* fail to do so enough times, the stack will overflow and no longer work. //* //* GroupRefresh cleans a group of any shadow references which may be clogging //* its hashtable. If you remove a unit from the game who is a member of a unit //* group, it will 'effectively' remove the unit from the group, but leave a //* shadow in its place. Calling GroupRefresh on a group will clean up any //* shadow references that may exist within it. It is only worth doing this on //* groups that you plan to have around for awhile. //* //* Constants that can be used from the library: //* [group] ENUM_GROUP As you might expect, this group is good for //* when you need a group just for enumeration. //* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important //* because a 'null' boolexpr in enumeration //* calls results in a leak. Use this instead. //* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness. //* //* This library also includes a simple implementation of a group enumeration //* call that factors collision of units in a given area of effect. This is //* particularly useful because GroupEnumUnitsInRange doesn't factor collision. //* //* In your map, you can just replace all instances of GroupEnumUnitsInRange //* with GroupEnumUnitsInArea with identical arguments and your spells will //* consider all units colliding with the area of effect. After calling this //* function as you would normally call GroupEnumUnitsInRange, you are free to //* do anything with the group that you would normally do. //* //* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE //* variable below and the library will use that as the added radius to check. //* If you use xebasic, however, the script will automatically use xe's //* collision size variable. //* //* You are also able to use GroupUnitsInArea. This function returns all units //* within the area, no matter what they are, which can be convenient for those //* instances where you actually want that. //* //* Example usage: //* local group MyGroup = NewGroup() //* call GroupRefresh(MyGroup) //* call ReleaseGroup(MyGroup) //* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE) //* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.) //* globals //If you don't have xebasic in your map, this value will be used instead. //This value corresponds to the max collision size of a unit in your map. private constant real MAX_COLLISION_SIZE = 197. //If you are insane and don't care about any of the protection involved in //this library, but want this script to be really fast, set this to true. private constant boolean LESS_SAFETY = false endglobals globals //* Constants that are available to the user group ENUM_GROUP = CreateGroup() boolexpr BOOLEXPR_TRUE = null boolexpr BOOLEXPR_FALSE = null endglobals globals //* Hashtable for debug purposes private hashtable ht = InitHashtable() //* Temporary references for GroupRefresh private boolean Flag = false private group Refr = null //* Arrays and counter for the group stack private group array Groups private integer Count = 0 //* Variables for use with the GroupUnitsInArea function private real X = 0. private real Y = 0. private real R = 0. private hashtable H = InitHashtable() endglobals private function HookDestroyGroup takes group g returns nothing if g == ENUM_GROUP then call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed") endif endfunction debug hook DestroyGroup HookDestroyGroup private function AddEx takes nothing returns nothing if Flag then call GroupClear(Refr) set Flag = false endif call GroupAddUnit(Refr, GetEnumUnit()) endfunction function GroupRefresh takes group g returns nothing set Flag = true set Refr = g call ForGroup(Refr, function AddEx) if Flag then call GroupClear(g) endif endfunction function NewGroup takes nothing returns group if Count == 0 then set Groups[0] = CreateGroup() else set Count = Count - 1 endif static if not LESS_SAFETY then call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1) endif return Groups[Count] endfunction function ReleaseGroup takes group g returns boolean local integer id = GetHandleId(g) static if LESS_SAFETY then if g == null then debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released") return false elseif Count == 8191 then debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group") call DestroyGroup(g) return false endif else if g == null then debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released") return false elseif not HaveSavedInteger(ht, 0, id) then debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack") return false elseif LoadInteger(ht, 0, id) == 2 then debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released") return false elseif Count == 8191 then debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group") call DestroyGroup(g) return false endif call SaveInteger(ht, 0, id, 2) endif call GroupClear(g) set Groups[Count] = g set Count = Count + 1 return true endfunction private function Filter takes nothing returns boolean return IsUnitInRangeXY(GetFilterUnit(), X, Y, R) endfunction private function HookDestroyBoolExpr takes boolexpr b returns nothing local integer bid = GetHandleId(b) if HaveSavedHandle(H, 0, bid) then //Clear the saved boolexpr call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid)) call RemoveSavedHandle(H, 0, bid) endif endfunction hook DestroyBoolExpr HookDestroyBoolExpr private constant function GetRadius takes real radius returns real static if LIBRARY_xebasic then return radius+XE_MAX_COLLISION_SIZE else return radius+MAX_COLLISION_SIZE endif endfunction function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing local real prevX = X local real prevY = Y local real prevR = R local integer bid = 0 //Set variables to new values set X = x set Y = y set R = radius if filter == null then //Adjusts for null boolexprs passed to the function set filter = Condition(function Filter) else //Check for a saved boolexpr set bid = GetHandleId(filter) if HaveSavedHandle(H, 0, bid) then //Set the filter to use to the saved one set filter = LoadBooleanExprHandle(H, 0, bid) else //Create a new And() boolexpr for this filter set filter = And(Condition(function Filter), filter) call SaveBooleanExprHandle(H, 0, bid, filter) endif endif //Enumerate, if they want to use the boolexpr, this lets them call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter) //Give back original settings so nested enumerations work set X = prevX set Y = prevY set R = prevR endfunction function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing local real prevX = X local real prevY = Y local real prevR = R //Set variables to new values set X = x set Y = y set R = radius //Enumerate call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter)) //Give back original settings so nested enumerations work set X = prevX set Y = prevY set R = prevR endfunction private function True takes nothing returns boolean return true endfunction private function False takes nothing returns boolean return false endfunction private function Init takes nothing returns nothing set BOOLEXPR_TRUE = Condition(function True) set BOOLEXPR_FALSE = Condition(function False) endfunction endlibrary The main code of this spell. It was written by Cjass. I will re-do it later. Bribe's post #8 below already shown the Zinc version of this code. Deprecated Cjass:scope HolyTorch initializer init { define ABILITY_ID = 'A000' // ID of the spell define ORDER_ID_ON = "immolation" // The order id of the spell define ORDER_ID_OFF = "unimmolation" // The close order id of the spell define RADIUS = 600.00 // Radius of the spell define ANGLE = 30.0 // This angle indicates the angle between the 2 lights define GAP = 2.5 // How often the units are affected. define MAX_SIMULTANEOUSLY = 16 // Only 16 such spell can be casted at the same time. Change it to any number under 8192(Exclusive) private keyword Node; private keyword UnitTimedInList; /** * After GAP sec. the unit will be removed from the list. */ private struct RemoveNodeFromList { private timer t; private Node node; private UnitTimedInList list; private static void execute() thistype this = GetTimerData(GetExpiredTimer()); this.list.remove(this.node.elem); this.destroy(); static thistype create(Node node, UnitTimedInList list, real cycle) thistype this = thistype.allocate(); this.node = node; this.list = list; this.t = NewTimer(); SetTimerData(this.t, this); TimerStart(this.t, cycle, false, function thistype.execute); return this; private void onDestroy() ReleaseTimer(this.t); } /** * Represents a node in the linked list. */ private struct Node { Node prev; Node next; unit elem; boolean hasNext() return (this.next != 0); static thistype create(unit u) thistype this = thistype.allocate(); this.elem = u; this.prev = 0; this.next = 0; return this; void onDestroy() this.prev = 0; this.next = 0; this.elem = null; } /** * The linked list which stores the units which are affected temporarily. * Function List: * static UnitTimedInList create(real cycle) * void add(unit u) * void finish() * E.g. * UnitTimedInList ul = UnitTimedInList.create(1.0); // Create an instance and set the time - 1.0 sec. * ul.add(unit1); // After 1.0 sec. this unit - unit1 will be removed from the list * ul.add(unit2); // After 1.0 sec. this unit - unit2 will be removed from the list * ul.finish(); // This list is deprecated, after the last unit is removed from the list * // this list will be destroyed automatically. */ struct UnitTimedInList { private Node head; private Node last; private boolean used; private real cycle; bool isEmpty() return (this.head == 0); private int locate(unit u) if (this.isEmpty()) { return -1; } int index = -1; Node iterator = head; boolean find = false; loop if (iterator.elem == u) { find = true; } else { iterator = iterator.next; } index++; exitwhen ((iterator == 0) or find) endloop if (find) { return index; } else { return -1; } boolean contains(unit u) return (this.locate(u) != -1); void add(unit u) if (this.contains(u)) { BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: unit " + GetUnitName(u) + " already exists in the list."); return; } if (this.used) { BJDebugMsg("|cff00ff00UnitTimedInList|r: is deprecated."); return; } Node newNode = Node.create(u); newNode.prev = this.last; if (this.isEmpty()) { this.head = newNode; this.last = this.head; } else { this.last.next = newNode; newNode.prev = this.last; this.last = newNode; } RemoveNodeFromList.create(newNode, this, this.cycle); void remove(unit u) if (not(this.contains(u))) { BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: unit " + GetUnitName(u) + " does not exist in the list."); return } int index = this.locate(u); Node tmp = this.head; loop exitwhen (index <= 0) tmp = tmp.next; index--; endloop if (index < 0) { BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: unit " + GetUnitName(u) + " cannot be removed from the list."); return } if (tmp.prev == 0 and tmp.next == 0) { this.head = 0; this.last = 0; } else { if (tmp.prev != 0) tmp.prev.next = tmp.next; else this.head = tmp.next; this.head.prev = 0; if (tmp.next != 0) tmp.next.prev = tmp.prev; else this.last = tmp.prev; this.last.next = 0; } tmp.destroy(); if (this.used and this.isEmpty()) { this.destroy(); BJDebugMsg("|cff00ff00UnitTimedInList|r: Ready to be destroyed.") } void finish() if (used) { BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: List is already ready to be destroyed."); return } this.used = true; static thistype create(real cycle) thistype this = thistype.allocate(); this.head = 0; this.last = 0; this.used = false; this.cycle = cycle; return this; } /** * The struct which manages the spell. */ struct Activated { private static Activated array instances; private static int index = 0; private static Activated tmp; private timer t; private unit u; private lightning leftHand; private lightning rightHand; private int level; private UnitTimedInList ul; /** * Manages the instances of this struct. */ private static void add(thistype act) int count = 0; loop thistype.index++; if (thistype.index >= MAX_SIMULTANEOUSLY) { thistype.index = 0; } exitwhen ((thistype.instances[thistype.index] == 0) or (count >= MAX_SIMULTANEOUSLY)) count++; endloop if (count >= MAX_SIMULTANEOUSLY) { BJDebugMsg("|cffff0000Exception - HolyTorch|r : exhausted. destroy!"); act.destroy(); } else { thistype.instances[thistype.index] = act; BJDebugMsg("|cff00ff00HolyTorch|r : successfully added, index number - " + I2S(thistype.index)); } static void kill(unit u) int i = 0; bool find = false; thistype tmp = 0; loop tmp = thistype.instances[i++]; if (tmp.u == u) { find = true; } exitwhen (find or (i >= MAX_SIMULTANEOUSLY)) endloop if (i > MAX_SIMULTANEOUSLY) { BJDebugMsg("|cffff0000Exception - HolyTorch|r : no such unit is holding an instance."); } elseif (find) { BJDebugMsg("|cff00ff00HolyTorch|r : destroying " + GetUnitName(tmp.u) + "'s instance - " + I2S(i - 1)); thistype.instances[i - 1] = 0; tmp.destroy(); } else { BJDebugMsg("|cffff0000Exception - HolyTorch|r : fatal error!"); } private static bool enumUnits() unit f = GetFilterUnit(); if (f == thistype.tmp.u) { f = null; return false; } if (IsUnitEnemy(f, GetOwningPlayer(thistype.tmp.u))) { f = null; return false; } if (IsUnitType(f, UNIT_TYPE_DEAD)) { f = null; return false; } if (thistype.tmp.ul.contains(f)) { f = null; return false; } real angle = bj_RADTODEG * Atan2((GetUnitY(thistype.tmp.u) - GetUnitY(f)), (GetUnitX(thistype.tmp.u) - GetUnitX(f))) + 180.0; //BJDebugMsg("Angle : " + R2S(angle)); real facing = GetUnitFacing(thistype.tmp.u); //BJDebugMsg("Facin : " + R2S(facing)); if ((RAbsBJ(angle - facing) <= ANGLE) or (RAbsBJ(RAbsBJ(angle - facing) - 360) <= ANGLE)) { SetWidgetLife(f, GetWidgetLife(f) + 10 * thistype.tmp.level); AddTimedEffectAtUnit.create("Abilities\\Spells\\Human\\Heal\\HealTarget.mdl", f, "origin", 1.0); thistype.tmp.ul.add(f); } f = null; return false; private static void moveLightningEfx() thistype this = GetTimerData(GetExpiredTimer()); .redirectLightning(); private void redirectLightning() real x1 = GetUnitX(.u); real y1 = GetUnitY(.u); real z = GetUnitFlyHeight(.u); real x2 = x1 + RADIUS * Cos(bj_DEGTORAD * (GetUnitFacing(u) + ANGLE / 2)); real y2 = y1 + RADIUS * Sin(bj_DEGTORAD * (GetUnitFacing(u) + ANGLE / 2)); if (.leftHand == null) { .leftHand = AddLightningEx("HWPB", false, x1, y1, z, x2, y2, z); } else { MoveLightningEx(.leftHand, false, x1, y1, z, x2, y2, z); } x2 = x1 + RADIUS * Cos(bj_DEGTORAD * (GetUnitFacing(u) - ANGLE / 2)); y2 = y1 + RADIUS * Sin(bj_DEGTORAD * (GetUnitFacing(u) - ANGLE / 2)); if (.rightHand == null) { .rightHand = AddLightningEx("HWPB", false, x1, y1, z, x2, y2, z); } else { MoveLightningEx(.rightHand, false, x1, y1, z, x2, y2, z); } .level = GetUnitAbilityLevel(.u, ABILITY_ID); thistype.tmp = this; GroupEnumUnitsInArea(ENUM_GROUP, GetUnitX(.u), GetUnitY(.u), RADIUS, Filter(function thistype.enumUnits)); static thistype create(unit u) thistype this = thistype.allocate(); .u = u; .t = NewTimer(); .leftHand = null; .rightHand = null; .redirectLightning(); .ul = UnitTimedInList.create(GAP); SetTimerData(.t, this); TimerStart(.t, 0.04, true, function thistype.moveLightningEfx); thistype.add(this); return this; private void onDestroy() ReleaseTimer(.t); .u = null; DestroyLightning(.leftHand); DestroyLightning(.rightHand); .ul.finish(); } private bool launch() { if (GetIssuedOrderId() == OrderId(ORDER_ID_ON)) Activated.create(GetTriggerUnit()); if (GetIssuedOrderId() == OrderId(ORDER_ID_OFF)) Activated.kill(GetTriggerUnit()); return false; } private void init() { trigger tg = CreateTrigger(); TriggerRegisterAnyUnitEventBJ(tg, EVENT_PLAYER_UNIT_ISSUED_ORDER); TriggerAddCondition(tg, Condition(function launch)); tg = null; } Add a textmacro in ListModule (2011/02/02 updated) Zinc://! textmacro WriteListModuleSearchInst takes t, s static method SearchInst($t$ elem) -> thistype { thistype tmp = thistype.first; boolean found = false; while (tmp != 0 && !found) { if (tmp.$s$ == elem) { found = true; } else { tmp = tmp.next; } } if (found) { return tmp; } else { debug BJDebugMsg("|cffff0000Exception|r: No such $t$ in list."); return 0; } } //! endtextmacro Well, I should use vJass syntax to do it. (2011/02/02 updated) And as the original idea, I put a unit in a group, and remove it from the group automatically. This time I make it as a separated library. (2011/02/02 updated) Zinc://! zinc library UnitTimedInGroup requires GroupUtils, TimerUtils { //*============================================================================= //* public struct UnitTimedInGroup //* static create(group) -> UnitTimedInGroup //* add(unit, real) -> nothing //* terminate() -> nothing //*============================================================================= private struct TimedRemove { private timer t; private group g; private unit u; private method destroy() { ReleaseTimer(this.t); this.t = null; this.u = null; this.g = null; this.deallocate(); } private static method execute() { thistype this = GetTimerData(GetExpiredTimer()); UnitTimedInGroup tmp = 0; GroupRemoveUnit(this.g, this.u); debug BJDebugMsg("Group removed unit"); tmp = UnitTimedInGroup.SearchInst(this.g); if (tmp.destroying && FirstOfGroup(this.g) == null) { debug BJDebugMsg("Start to destroy UnitTimedInGroup"); tmp.destroy(); } this.destroy(); } static method create(group g, unit u, real dur) -> thistype { thistype this = thistype.allocate(); this.g = g; this.u = u; this.t = NewTimer(); SetTimerData(this.t, this); TimerStart(this.t, dur, false, function thistype.execute); return this; } } public struct UnitTimedInGroup { module List; group g; boolean destroying; //! runtextmacro WriteListModuleSearchInst("group", "g") static method create(group g) -> thistype { thistype this = thistype.allocate(); this.g = g; this.destroying = false; this.listAdd(); debug BJDebugMsg("New UnitTimedInGroup"); return this; } method add(unit u, real dur) { if (IsUnitInGroup(u, this.g) || this.destroying) { debug BJDebugMsg("Unit already in the group or is destroying"); } else { GroupAddUnit(this.g, u); debug BJDebugMsg("Added unit"); TimedRemove.create(this.g, u, dur); } } method terminate() { debug BJDebugMsg("Prepared to destroy UnitTimedInGroup"); this.destroying = true; if (FirstOfGroup(this.g) == null) { debug BJDebugMsg("Start to destroy UnitTimedInGroup"); this.destroy(); } } method destroy() { ReleaseGroup(this.g); this.g = null; this.listRemove(); this.deallocate(); debug BJDebugMsg("Destroyed UnitTimedInGroup"); } } } //! endzinc And this is the main spell. The way I calculate the trigonometric functions may not be that nice. (2011/02/02 updated) Zinc://! zinc library HolyTorch requires UnitTimedInGroup { private constant integer ABILITY_ID = 'A000'; // ID of the spell private constant string ORDER_ID_ON = "immolation"; // The order id of the spell private constant string ORDER_ID_OFF = "unimmolation"; // The close order id of the spell private constant real RADIUS = 600.0; // Radius of the spell private constant real ANGLE = bj_PI / 6.0; // This angle indicates the angle between the 2 lights private constant real GAP = 2.5; // How often the units are affected. /** * The struct that manages the spell. */ struct Activated { module List; private static Activated tmp; private timer t; private unit u; private lightning leftHand; private lightning rightHand; private integer level; private UnitTimedInGroup ul; //! runtextmacro WriteListModuleSearchInst("unit", "u") private static method enumUnits() -> boolean { unit f = GetFilterUnit(); real angle = 0.0; real facing = 0.0; if (f == thistype.tmp.u) { f = null; return false; } if (IsUnitEnemy(f, GetOwningPlayer(thistype.tmp.u))) { f = null; return false; } if (IsUnitType(f, UNIT_TYPE_DEAD)) { f = null; return false; } if (IsUnitInGroup(f, thistype.tmp.ul.g)) { f = null; return false; } angle = bj_RADTODEG * Atan2((GetUnitY(thistype.tmp.u) - GetUnitY(f)), (GetUnitX(thistype.tmp.u) - GetUnitX(f))) + 180.0; //BJDebugMsg("Angle : " + R2S(angle)); facing = GetUnitFacing(thistype.tmp.u); //BJDebugMsg("Facin : " + R2S(facing)); if ((RAbsBJ(angle - facing) <= ANGLE * bj_RADTODEG) || (RAbsBJ(RAbsBJ(angle - facing) - 360) <= ANGLE * bj_RADTODEG)) { SetWidgetLife(f, GetWidgetLife(f) + 10 * thistype.tmp.level); AddTimedEffectAtUnit.create("Abilities\\Spells\\Human\\Heal\\HealTarget.mdl", f, "origin", 1.0); thistype.tmp.ul.add(f, GAP); } f = null; return false; } private static method moveLightningEfx() { thistype this = GetTimerData(GetExpiredTimer()); this.redirectLightning(); } private method redirectLightning() { real x1 = GetUnitX(this.u); real y1 = GetUnitY(this.u); real z = GetUnitFlyHeight(this.u); real x2 = x1 + RADIUS * Cos(bj_DEGTORAD * (GetUnitFacing(u)) + ANGLE / 2); real y2 = y1 + RADIUS * Sin(bj_DEGTORAD * (GetUnitFacing(u)) + ANGLE / 2); if (this.leftHand == null) { this.leftHand = AddLightningEx("HWPB", false, x1, y1, z, x2, y2, z); } else { MoveLightningEx(this.leftHand, false, x1, y1, z, x2, y2, z); } x2 = x1 + RADIUS * Cos(bj_DEGTORAD * (GetUnitFacing(u)) - ANGLE / 2); y2 = y1 + RADIUS * Sin(bj_DEGTORAD * (GetUnitFacing(u)) - ANGLE / 2); if (this.rightHand == null) { this.rightHand = AddLightningEx("HWPB", false, x1, y1, z, x2, y2, z); } else { MoveLightningEx(this.rightHand, false, x1, y1, z, x2, y2, z); } this.level = GetUnitAbilityLevel(this.u, ABILITY_ID); thistype.tmp = this; GroupEnumUnitsInArea(ENUM_GROUP, GetUnitX(this.u), GetUnitY(this.u), RADIUS, Filter(function thistype.enumUnits)); } static method create(unit u) -> thistype { thistype this = thistype.allocate(); this.u = u; this.t = NewTimer(); this.leftHand = null; this.rightHand = null; this.ul = UnitTimedInGroup.create(NewGroup()); this.redirectLightning(); this.listAdd(); SetTimerData(this.t, this); TimerStart(this.t, 0.04, true, function thistype.moveLightningEfx); return this; } static method kill(unit u) { thistype tmp = thistype.SearchInst(u); tmp.listRemove(); tmp.destroy(); } private method destroy() { ReleaseTimer(this.t); this.u = null; this.t = null; DestroyLightning(this.leftHand); DestroyLightning(this.rightHand); this.leftHand = null; this.rightHand = null; this.ul.terminate(); this.deallocate(); } } private function launch() -> boolean { if (GetIssuedOrderId() == OrderId(ORDER_ID_ON)) { Activated.create(GetTriggerUnit()); } if (GetIssuedOrderId() == OrderId(ORDER_ID_OFF)) { Activated.kill(GetTriggerUnit()); } return false; } private function onInit() { trigger tg = CreateTrigger(); TriggerRegisterAnyUnitEventBJ(tg, EVENT_PLAYER_UNIT_ISSUED_ORDER); TriggerAddCondition(tg, Condition(function launch)); tg = null; } } //! endzinc Sometimes the structs seem not to be destroyed properly. This time it works perfectly -- so far. (2011/02/02 updated) Solved Problem:[s]And please tell me how to use Cjass wraps in WC3C[/s] It is nothing:By the way, please don't mind some of the comments or the skills instructions due to my bad English. FInally, I did it, as a promise.(2011/02/02 updated) |
| 01-08-2011, 02:08 PM | #2 |
Put it in Zinc tags, the redness makes it unreadable |
| 01-09-2011, 11:09 AM | #3 | |
Quote:
Since NewGen supports Cjass. why WC3C do not support Cjass wraps? It makes my life difficult |
| 01-09-2011, 09:02 PM | #4 | |
Quote:
Because the bugs in cjass were never fixed... if you read through feedback, you'll see them all. |
| 01-09-2011, 10:54 PM | #5 | |
Quote:
Could you please show me some references of them?. I mean what bugs are there in Cjass. Since I knew Cjass syntaxes, I can hardly convert my behaviours into Vjass syntaxes. Too many "set"s, "local"s consume time. |
| 01-10-2011, 03:58 AM | #6 | |
Quote:
and flush locals is ofc bugged |
| 01-10-2011, 04:52 AM | #7 |
I heard textmacros don't work in cJass, either. |
| 01-10-2011, 06:04 AM | #8 |
Zinc:library HolyTorch { constant integer ABILITY_ID = 'A000'; // ID of the spell constant integer ORDER_ID_ON = OrderId("immolation"); // The order id of the spell constant integer ORDER_ID_OFF = OrderId("unimmolation"); // The close order id of the spell constant real RADIUS = 600.00; // Radius of the spell constant real ANGLE = 30.0; // This angle indicates the angle between the 2 lights constant real GAP = 2.5; // How often the units are affected. constant integer MAX_SIMULTANEOUSLY = 16; // Only 16 such spell can be casted at the same time. Change it to any number under 8192(Exclusive) /** * Represents a node in the linked list. */ struct Node { Node prev; Node next; unit elem; method hasNext() -> boolean { return (this.next != 0); } static method create(unit u) -> thistype { thistype this = thistype.allocate(); this.elem = u; this.prev = 0; this.next = 0; return this; } method destroy() { this.prev = 0; this.next = 0; this.elem = null; this.deallocate() } } /** * After GAP sec. the unit will be removed from the list. */ struct RemoveNodeFromList { Node node; integer list; static method execute() { timer t = GetExpiredTimer(); thistype this = GetTimerData(t); removeListProxy(this.list, this.node.elem).evaluate(); ReleaseTimer(t); this.deallocate(); t = null; } static method create(Node node, integer list, real cycle) -> thistype { thistype this = thistype.allocate(); timer t = NewTimer(); this.node = node; this.list = list; SetTimerData(t, this); TimerStart(t, cycle, false, function thistype.execute); t = null; return this; } } /** * The linked list which stores the units which are affected temporarily. * Function List: * static UnitTimedInList create(real cycle) * method add(unit u) * method finish() * E.g. * UnitTimedInList ul = UnitTimedInList.create(1.0); // Create an instance and set the time - 1.0 sec. * ul.add(unit1); // After 1.0 sec. this unit - unit1 will be removed from the list * ul.add(unit2); // After 1.0 sec. this unit - unit2 will be removed from the list * ul.finish(); // This list is deprecated, after the last unit is removed from the list * // this list will be destroyed automatically. */ struct UnitTimedInList { private Node head; private Node last; private boolean used; private real cycle; method isEmpty() -> boolean { return (this.head == 0); } method locate(unit u) -> integer { integer index = -1; Node iterator = head; boolean find = false; if (this.isEmpty()) { return -1; } while (true) { if (iterator.elem == u) { break; } else { iterator = iterator.next; } index+=1; if (iterator == 0) break; } if (find) { return index; } else { return -1; } } method contains(unit u) -> boolean { return (this.locate(u) != -1); } method add(unit u) { Node newNode; if (this.contains(u)) { debug BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: unit " + GetUnitName(u) + " already exists in the list."); return; } if (this.used) { debug BJDebugMsg("|cff00ff00UnitTimedInList|r: is deprecated."); return; } newNode = Node.create(u); newNode.prev = this.last; if (this.isEmpty()) { this.head = newNode; this.last = this.head; } else { this.last.next = newNode; newNode.prev = this.last; this.last = newNode; } RemoveNodeFromList.create(newNode, this, this.cycle); } method remove(unit u) { integer index; Node tmp = this.head; if (!(this.contains(u))) { debug BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: unit " + GetUnitName(u) + " does not exist in the list."); return } index = this.locate(u); while (index <= 0) { tmp = tmp.next; index-=1; } if (index < 0) { debug BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: unit " + GetUnitName(u) + " cannot be removed from the list."); return } if (tmp.prev == 0 && tmp.next == 0) { this.head = 0; this.last = 0; } else { if (tmp.prev != 0) { tmp.prev.next = tmp.next; } else { this.head = tmp.next; this.head.prev = 0; } if (tmp.next != 0) { tmp.next.prev = tmp.prev; } else { this.last = tmp.prev; this.last.next = 0; } } tmp.destroy(); if (this.used && this.isEmpty()) { this.destroy(); debug BJDebugMsg("|cff00ff00UnitTimedInList|r: Ready to be destroyed.") } } method finish() { if (used) debug BJDebugMsg("|cffff0000Exception in UnitTimedInList|r: List is already ready to be destroyed."); else used = true; } static method create(real cycle) -> thistype { thistype this = thistype.allocate(); this.head = 0; this.last = 0; this.used = false; this.cycle = cycle; return this; } } /** * The struct which manages the spell. */ struct Activated { static Activated instances[]; static integer index = 0; static Activated tmp; timer t; unit u; lightning leftHand; lightning rightHand; integer level; UnitTimedInList ul; method destroy() { ReleaseTimer(this.t); this.u = null; DestroyLightning(this.leftHand); DestroyLightning(this.rightHand); this.ul.finish(); this.deallocate() } /** * Manages the instances of this struct. */ static method add(thistype act) { integer count = 0; while (true) { thistype.index+=1; if (thistype.index >= MAX_SIMULTANEOUSLY) thistype.index = 0; if ((thistype.instances[thistype.index] == 0) || (count >= MAX_SIMULTANEOUSLY)) break; count+=1; } if (count >= MAX_SIMULTANEOUSLY) { debug BJDebugMsg("|cffff0000Exception - HolyTorch|r : exhausted. destroy!"); act.destroy(); } else { thistype.instances[thistype.index] = act; debug BJDebugMsg("|cff00ff00HolyTorch|r : successfully added, index number - " + I2S(thistype.index)); } } static method kill(unit u) { integer i = 0; boolean find = false; thistype tmp = 0; while (true) { i+=1; tmp = thistype.instances[i]; if (tmp.u == u) find = true; if (find || (i >= MAX_SIMULTANEOUSLY)) break; } if (i > MAX_SIMULTANEOUSLY) { debug BJDebugMsg("|cffff0000Exception - HolyTorch|r : no such unit is holding an instance."); } else if (find) { debug BJDebugMsg("|cff00ff00HolyTorch|r : destroying " + GetUnitName(tmp.u) + "'s instance - " + I2S(i - 1)); thistype.instances[i - 1] = 0; tmp.destroy(); } debug else { BJDebugMsg("|cffff0000Exception - HolyTorch|r : fatal error!"); } } static method enumUnits() -> boolean { unit f = GetFilterUnit(); real angle real facing if (f == thistype.tmp.u) { f = null; return false; } if (IsUnitEnemy(f, GetOwningPlayer(thistype.tmp.u))) { f = null; return false; } if (IsUnitType(f, UNIT_TYPE_DEAD)) { f = null; return false; } if (thistype.tmp.ul.contains(f)) { f = null; return false; } angle = bj_RADTODEG * Atan2((GetUnitY(thistype.tmp.u) - GetUnitY(f)), (GetUnitX(thistype.tmp.u) - GetUnitX(f))) + 180.0; //BJDebugMsg("Angle : " + R2S(angle)); facing = GetUnitFacing(thistype.tmp.u); //BJDebugMsg("Facin : " + R2S(facing)); if ((RAbsBJ(angle - facing) <= ANGLE) || (RAbsBJ(RAbsBJ(angle - facing) - 360) <= ANGLE)) { SetWidgetLife(f, GetWidgetLife(f) + 10 * thistype.tmp.level); AddTimedEffectAtUnit.create("Abilities\\Spells\\Human\\Heal\\HealTarget.mdl", f, "origin", 1.0); thistype.tmp.ul.add(f); } f = null; return false; } method redirectLightning() { real x1 = GetUnitX(u); real y1 = GetUnitY(u); real z = GetUnitFlyHeight(u); real x2 = x1 + RADIUS * Cos(bj_DEGTORAD * (GetUnitFacing(u) + ANGLE / 2)); real y2 = y1 + RADIUS * Sin(bj_DEGTORAD * (GetUnitFacing(u) + ANGLE / 2)); if (leftHand == null) { leftHand = AddLightningEx("HWPB", false, x1, y1, z, x2, y2, z); } else { MoveLightningEx(leftHand, false, x1, y1, z, x2, y2, z); } x2 = x1 + RADIUS * Cos(bj_DEGTORAD * (GetUnitFacing(u) - ANGLE / 2)); y2 = y1 + RADIUS * Sin(bj_DEGTORAD * (GetUnitFacing(u) - ANGLE / 2)); if (rightHand == null) { rightHand = AddLightningEx("HWPB", false, x1, y1, z, x2, y2, z); } else { MoveLightningEx(rightHand, false, x1, y1, z, x2, y2, z); } this.level = GetUnitAbilityLevel(u, ABILITY_ID); thistype.tmp = this; GroupEnumUnitsInArea(ENUM_GROUP, GetUnitX(u), GetUnitY(u), RADIUS, Filter(function thistype.enumUnits)); } static method moveLightningEfx() { thistype(GetTimerData(GetExpiredTimer())).redirectLightning(); } static method create(unit u) -> thistype { thistype this = thistype.allocate(); this.u = u; this.t = NewTimer(); this.leftHand = null; this.rightHand = null; this.redirectLightning(); this.ul = UnitTimedInList.create(GAP); SetTimerData(this.t, this); TimerStart(this.t, 0.04, true, function thistype.moveLightningEfx); thistype.add(this); return this; } } function removeListProxy(UnitTimedInList list, unit elem) { list.remove(elem); } function launch() -> boolean { if (GetIssuedOrderId() == ORDER_ID_ON) { Activated.create(GetTriggerUnit()); } else if (GetIssuedOrderId() == ORDER_ID_OFF) { Activated.kill(GetTriggerUnit()); } return false; } function onInit() { trigger tg = CreateTrigger(); TriggerRegisterAnyUnitEventBJ(tg, EVENT_PLAYER_UNIT_ISSUED_ORDER); TriggerAddCondition(tg, Condition(function launch)); tg = null; } } Please check for syntax errors as this is converted to Zinc from my memory of Zinc and working around the existing bugs ("features") of Zinc. Please do not ever ask for help with cJass scripts as that is not a supported language as it is totally broken. onDestroy - never use! Use the plain destroy method and from it call deallocate. keywords, I don't remember if they work in Zinc so I made a proxy function to avoid typecasting errors. In regards to the thing not being destroyed properly, I recommend you put bj debug messages in all of your destroy functions and also right before you destroy, which say "expected to destroy type of X" before destroying and, from the destroy method, "destroyed type of X". If the type X do not match, you are destroying the wrong type of struct. |
| 01-10-2011, 08:56 AM | #9 | |
To: Nestharus #6 Thank you for the two links, although I do not really comprehend your posts. But I made a note, some day they will be useful :) And I never use "flush locals", because I did not trust them initially somehow. To: Bribe #8 I greatly appreciate this. And thank you for the suggestions on "Destroy"s. And by the way. Quote:
I'm not sure but textmacros work well in Cjass in some of my previous works. And when "//! endtextmacro" became the last line of a trigger. sometimes it does not work properly |
| 01-10-2011, 12:34 PM | #10 | |
Quote:
The spell is kinda long. It would be a lot easier to debug if it used an outside linked list library like LinkedList or Stack. Actually, since you're storing units, you could just use a unit group, there's no need for using a linked list in the first place. If the bug remains after these changes, it should be a lot easier to find. |
| 01-10-2011, 12:48 PM | #11 |
@yatyfonrnetreg JASS:library PlayerManager uses /* */Event //hiveworkshop.com/forums/submissions-414/snippet-event-186555/ endlibrary The /*\n*/ bugs in cjass ;P. JASS:static if true then local real r endif That will get declared twice because cjass doesn't see it at the top of the script. JASS:local real r static if true then local real r endif |
| 01-11-2011, 06:22 AM | #12 | |
To: Anitarf #10 Hmm.. yes, it was me who is complexifying the problem. Thank you for the suggestion and the two libraries. Quote:
To: Nestharus #11 Yes, I can see it now. And I will avoid them. Thank you. To: Bribe #8 I get used with Zinc syntax now, it is more strict than Cjass, which is good. For example, the semi-colon is necessary in Zinc but not in Cjass. And the for loop and while loop is awesome. I like them. Thank you for telling that. |
