| 02-27-2009, 09:02 PM | #1 | |
Background: I was in need of an Assistance System which could handle multiple Heros/Units per player. Since i havent found any good system which achieves that, i made this. Requirements:
Details:
jAssyst 1.5:library jAssyst initializer oninit uses GetPlayerColored, TimerUtils, UnitIndexingUtils //or AutoIndex /*// ====================================================================================== 'jAssyst VER 1.5' by Akolyt0r (10.03.2010) REQUIREMENTS: + vJass Precompiler (e.g. Jasshelper) + GetPlayerColored by Ammorth + UnitIndexingUtils by Rising_Dusk (can be changed easily to other indexing Systems) OPTIONAL: There are two modules for registering different events as "assist". You can use one, two, or none of them. (When you dont use any of them you will have to register all "assists" yourself !) + DamageDetect Available for 3 Damage Detection Systems at the moment. Use only 1 (ONE) flavor of this module at once. - LLDD ("Light Leakless Damage Detect") by Captain_Griffen - "Intuitive Damage Detection System" by Rising_Dusk - "ADamage" by Anitarf + SpellBonus Most maps contain aswell spells which dont deal any damage, So this module is for registering such spells (for example Slow or Curse) as assists aswell. USAGE: call RegisterAssistUnit(unit) // Registers a unit with the system, every attack by enemy units // against this unit will be counted as assist, when that attack happened in the last // MAX_ASSIST_DELAY seconds before the death of the unit. call UnRegisterAssistUnit(unit) //< Unregisters a unit with the system, ADDITIONAL FUNCTIONS (for Advanced Users): IsAssistUnitRegistered(unit) //< self explanatory.. RegisterAssist(unit assistingUnit, unit victimUnit) // Registers an assist: assistingUnit helped to try to kill victimUnit.. // So if victimUnit dies in less than MAX_ASSIST_DELAY seconds, assistingUnit will be // displayed as assisting unit. // victimUnit has to be registered with the systems RegisterAssistUnit // For most cases the existing modules should be sufficient, so you probably wont have to call // this method yourself. */// ====================================================================================== globals private constant integer MAX_FOE_PLAYERID = 16 //"Highest Player" (=>PlayerId) you want to include, examples: // 12 for maps with 12 players (without neutral units) // 16 for maps with 12 players (with neutral creeps) // 4 for maps with 4 players (WITHOUT neutral creeps) // 16 for maps with 4 players (WITH neutral creeps) // ... if you are unsure keep 16.. private constant integer MAXIMUM_ASSIST_UNITS = (8190/MAX_FOE_PLAYERID)-1 // Maximum ammount of units registered with the sytem // Best performance with MAXIMUM_ASSIST_UNITS smaller than 8190/MAX_FOE_PLAYERID // since most maps will only register assists for hero units, and i never saw // a map with more than (8190/16 =) 510 heroes, this shouldnt be a problem private constant real MAX_ASSIST_DELAY = 25. // only damage dealt in the last MAX_ASSIST_DELAY seconds is considered an assist private real UNREGISTER_TIMEOUT = 120. private boolean UNREGISTER_DEAD_UNITS = true // will automatically unregister dead, NONHERO, units after UNREGISTER_TIMEOUT seconds private constant string AND = " and " // used to format the display private constant string COMMA = ", " // of the assists, you can play with these, but these should work. endglobals //======== //// These functions are called to build the strings to be printed when the death of an registered unit occurs. // Edit to fit your needs private function GetSuicideMessage takes unit dieingUnit, player VictimAndKiller returns string return GetPlayerNameColored(VictimAndKiller)+" killed his "+GetUnitName(dieingUnit)+" himself." endfunction private function GetDenialMessage takes unit dieingUnit, player dieUOwner, unit killingUnit, player killUOwner returns string return GetPlayerNameColored(killUOwner)+" denied "+GetPlayerNameColored(dieUOwner)+"'s "+GetUnitName(dieingUnit)+"." endfunction //string AssistingPlayers contains the assisting players separated by the constants COMMA and AND private function GetAssistsMessage takes unit dieingUnit, player dieUOwner, unit killingUnit, player killUOwner, string AssistingPlayers, integer totalAssists returns string return GetPlayerNameColored(killUOwner)+" killed "+GetPlayerNameColored(dieUOwner)+"'s "+GetUnitName(dieingUnit)+", "+AssistingPlayers+" assisted." endfunction private function GetNoAssistsMessage takes unit dieingUnit, player dieUOwner, unit killingUnit, player killUOwner returns string return GetPlayerNameColored(killUOwner)+" killed "+GetPlayerNameColored(dieUOwner)+"'s "+GetUnitName(dieingUnit)+"." endfunction // dieUOwner == Owner of dieing Unit // killUOwner == Owner of killing Unit ///////======== private struct Players //struct to pass the players who helped boolean array PlayerAssisted[MAX_FOE_PLAYERID] // PlayerAssisted[player_id] will be true if Player(player_id) assisted endstruct //======== ////The function below will be called upon Death of a registered Unit private function KillRegistered takes unit dieingUnit, player Victim, unit killingUnit, player Killer, Players assistingPlayers returns nothing // this function will only be called for (dieing)Units which are registered with RegisterAssistUnit, // will not be called for suicides. // the argument assistingPlayers contains all players who helped to kill the (dieing)Unit. (Players struct, see above) // you can add your own stuff here //...like giving bonus gold for players which helped to kill an enemy hero. call assistingPlayers.destroy() //leave this endfunction //// ///////======== globals private integer array DATA[MAXIMUM_ASSIST_UNITS] // this variable only be used by the to functions below, // so you can ommit this easily, if you use another method // to store the data endglobals // You can edit these functions (together with DATA) to use another library instead of UnitIndexingUtils // e.g. PUI, Table (hashtable-version recommended for 1.23b and above), H2I(u)-OFFSET (not recommended), or whatever you want. private function getStruct takes unit u returns integer return DATA[GetUnitId(u)] endfunction private function setStruct takes unit u, integer data returns nothing set DATA[GetUnitId(u)] = data endfunction //////=== END OFCONFIGURATION ======================================================================= //////=== PRIVATE STRUCTS =========================================================================== globals private constant integer AP_STRUCT_SIZE = MAXIMUM_ASSIST_UNITS*MAX_FOE_PLAYERID endglobals private struct AP[AP_STRUCT_SIZE] readonly boolean active = false private timer t private boolean destroyed = true private static code timerCB private static method timerExpire takes nothing returns nothing local timer t = GetExpiredTimer() local AP this = GetTimerData(t) set this.active = false set t = null endmethod method setActive takes boolean act returns nothing if this.active then call PauseTimer(this.t) endif set this.active = act if act then call TimerStart(this.t,MAX_ASSIST_DELAY,false,function AP.timerExpire) endif endmethod static method create takes nothing returns AP local AP this = AP.allocate() set this.t = NewTimer() set this.active = false call SetTimerData(this.t,this) return this endmethod method terminate takes nothing returns nothing call ReleaseTimer(this.t) call this.destroy() endmethod endstruct private struct AssistUnit[MAXIMUM_ASSIST_UNITS] unit u force Aplayers timer aut AP array APs[MAX_FOE_PLAYERID] static group MU static method create takes nothing returns AssistUnit local AssistUnit this = AssistUnit.allocate() set this.u = null set this.aut = null return this endmethod private method onDestroy takes nothing returns nothing local integer i = 0 if this.aut != null then call ReleaseTimer(this.aut) set this.aut = null endif set this.u = null loop exitwhen i == MAX_FOE_PLAYERID call this.APs[i].terminate() set i = i + 1 endloop call GroupRemoveUnit(AssistUnit.MU,this.u) endmethod endstruct //////=== PUBLIC FUNCTIONS =========================================================================== function IsAssistUnitRegistered takes unit u returns boolean return IsUnitInGroup(u,AssistUnit.MU) endfunction function RegisterAssistUnit takes unit u returns nothing local AssistUnit this = AssistUnit.create() local integer i = 0 loop exitwhen i==MAX_FOE_PLAYERID set this.APs[i]=AP.create() set i = i + 1 endloop set this.u = u if this.Aplayers == null then set this.Aplayers = CreateForce() endif debug if IsUnitInGroup(u,AssistUnit.MU) then debug call BJDebugMsg(SCOPE_PREFIX+"Error: unit: \""+GetUnitName(u)+"\" already registered !") debug endif call GroupAddUnit(AssistUnit.MU,u) call setStruct(u,this) endfunction function UnRegisterAssistUnit takes unit u returns boolean local AssistUnit this = getStruct(u) if this.u != u then debug call BJDebugMsg(SCOPE_PREFIX+"Error: This unit: \""+GetUnitName(u)+"\" has not been registered with this system !") return false endif call this.destroy() return true endfunction function RegisterAssist takes unit assistingUnit, unit victimUnit returns nothing local player vicUOwner = GetOwningPlayer(victimUnit) local player assUOwner = GetOwningPlayer(assistingUnit) local integer pid = GetPlayerId(assUOwner) local AssistUnit this = getStruct(victimUnit) if this.u == victimUnit then call this.APs[pid].setActive(true) debug else debug call BJDebugMsg(SCOPE_PREFIX+"_RegisterAssist_Error: This unit: \""+GetUnitName(victimUnit)+"\" has not been registered with this system !") endif endfunction //////=== PRIVATE FUNCTIONS =========================================================================== private function unRegisterAUTimed takes nothing returns nothing local timer t = GetExpiredTimer() local AssistUnit this = GetTimerData(t) if IsUnitType(this.u,UNIT_TYPE_DEAD)==true then call this.destroy() endif call ReleaseTimer(t) set this.aut = null set t = null endfunction private function onDeath takes nothing returns nothing local unit dieingUnit = GetDyingUnit() local player dieUOwner = GetOwningPlayer(dieingUnit) local unit killingUnit = GetKillingUnit() local player killUOwner = GetOwningPlayer(killingUnit) local integer i = 0 local string text = "" local integer array pids local integer pcount = 0 local AssistUnit this local Players assistingPlayers local timer t if IsUnitInGroup(dieingUnit,AssistUnit.MU) then set this = getStruct(dieingUnit) if killUOwner != dieUOwner then if IsPlayerEnemy(killUOwner,dieUOwner) then set assistingPlayers = Players.create() loop exitwhen i == MAX_FOE_PLAYERID if this.APs[i].active and Player(i) != killUOwner then call this.APs[i].setActive(false) set pids[pcount] = i set pcount = pcount + 1 set assistingPlayers.PlayerAssisted[i] = true else set assistingPlayers.PlayerAssisted[i] = false endif set i = i + 1 endloop call KillRegistered(this.u,dieUOwner,killingUnit,killUOwner,assistingPlayers) set i = 0 loop exitwhen i == pcount set text = text + GetPlayerNameColored(Player(pids[i])) if pcount-i==2 then set text = text + AND elseif pcount-i >2 then set text = text + COMMA endif set i = i + 1 endloop if pcount == 0 then set text = GetNoAssistsMessage(this.u,dieUOwner,killingUnit,killUOwner) else set text = GetAssistsMessage(this.u,dieUOwner,killingUnit,killUOwner,text,pcount) endif else set text = GetDenialMessage(this.u,dieUOwner,killingUnit,killUOwner) endif else set text = GetSuicideMessage(this.u,dieUOwner) endif call DisplayTextToPlayer(GetLocalPlayer(),0,0,text) if UNREGISTER_DEAD_UNITS then if IsUnitType(dieingUnit,UNIT_TYPE_HERO)==false then if this.aut == null then set this.aut = NewTimer() call SetTimerData(t,this) call TimerStart(this.aut,UNREGISTER_TIMEOUT,false,function unRegisterAUTimed) else //Unit died again ... so we have to reset the timeout call PauseTimer(this.aut) call TimerStart(this.aut,UNREGISTER_TIMEOUT,false,function unRegisterAUTimed) endif endif endif endif set killingUnit = null set dieingUnit = null endfunction private function oninit takes nothing returns nothing local trigger t = CreateTrigger() set AssistUnit.MU = CreateGroup() call TriggerAddAction(t, function onDeath) call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_DEATH) endfunction endlibrary jAssyst SpellBonus Module:library jAssystSpellBonus initializer oninit requires jAssyst // ***************************************************************************************** //! novjass jAssyst SpellBonus Module by Akolyt0r (29.03.2009) ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ REQUIREMENTS: + vJass Precompiler (e.g. Jasshelper) + jAssyst, doesnt matter which version (BASIC or DDS) ABOUT: This Module is for registering spells which dont deal any damage as assists aswell (e.g Cripple or Curse). (Minor Problem: This will register spells which deal damage aswell, so they will be registered twice, but that doesnt matter at all.) //! endnovjass // ***************************************************************************************** private function SpellRegistered takes nothing returns boolean local unit u = GetTriggerUnit() local unit targ = GetSpellTargetUnit() if targ!=null then // its a single target spell! if IsPlayerEnemy(GetOwningPlayer(u),GetOwningPlayer(targ)) and IsAssistUnitRegistered(targ) then // Players are Enemies and Target Unit is registered // with the jAssyst-System, so lets register the assist. call RegisterAssist(u,targ) endif endif set u = null set targ = null return false endfunction private function oninit takes nothing returns nothing local trigger t = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(t,Condition(function SpellRegistered)) endfunction endlibrary jAssystModule DamageDetect for LLDD:library jAssystDamageDetectModule initializer oninit requires jAssyst, LightLeaklessDamageDetect // ***************************************************************************************** //! novjass 'jAssyst DamageDetection Module LLDD' by Akolyt0r (15.06.2009) REQUIREMENTS: + jAssyst + LLDD (Light Leakless Damage Detect) by Captain Griffen + vJass Precompiler (e.g. Jasshelper) ABOUT: This Module is a wrapper to enable you to choose the DDS of your choice, for registering damage events as assist. //! endnovjass // ***************************************************************************************** globals private constant real DAMAGE_THRESHOLD = 1. //wont register any damage below this threshold as assist private boolean ALLIED_DAMAGE_IsAssist = false // ...determines if "allied damage" can be treated as "assist" endglobals private function onDamage takes nothing returns boolean local unit damagedUnit = GetTriggerUnit() local unit damageSource = GetEventDamageSource() if IsAssistUnitRegistered(damagedUnit) then if GetEventDamage() > DAMAGE_THRESHOLD and((not ALLIED_DAMAGE_IsAssist) or ALLIED_DAMAGE_IsAssist==IsPlayerAlly(GetOwningPlayer(damagedUnit),GetOwningPlayer(damageSource))) then call RegisterAssist(damageSource,damagedUnit) endif endif set damagedUnit = null set damageSource = null return false endfunction private function oninit takes nothing returns nothing call AddOnDamageFunc(Condition(function onDamage)) endfunction endlibrary jAssystModule DamageDetect for IDDS:library jAssystDamageDetectModule initializer oninit requires jAssyst, IntuitiveDamageSystem // ***************************************************************************************** //! novjass 'jAssyst DamageDetection Module IDDS' by Akolyt0r (15.06.2009) REQUIREMENTS: + jAssyst + Intuitive Damage Detection System by Rising Dusk + vJass Precompiler (e.g. Jasshelper) ABOUT: This Module is a wrapper to enable you to choose the DDS of your choice, for registering damage events as assist. //! endnovjass // ***************************************************************************************** globals private constant real DAMAGE_THRESHOLD = 1. //wont register any damage below this threshold as assist private boolean ALLIED_DAMAGE_IsAssist = false // ...determines if "allied damage" can be treated as "assist" endglobals private function onDamage takes nothing returns boolean local unit damagedUnit = GetTriggerUnit() local unit damageSource = GetEventDamageSource() if IsAssistUnitRegistered(damagedUnit) then if GetEventDamage() > DAMAGE_THRESHOLD and((not ALLIED_DAMAGE_IsAssist) or ALLIED_DAMAGE_IsAssist==IsPlayerAlly(GetOwningPlayer(damagedUnit),GetOwningPlayer(damageSource))) then call RegisterAssist(damageSource,damagedUnit) endif endif set damagedUnit = null set damageSource = null return false endfunction private function oninit takes nothing returns nothing local trigger t = CreateTrigger() call TriggerAddCondition(t,Condition(function onDamage)) call TriggerRegisterDamageEvent(t,0) endfunction endlibrary jAssystModule DamageDetect for ADamage:library jAssystDamageDetectModule initializer oninit requires jAssyst, ADamage // ***************************************************************************************** //! novjass 'jAssyst DamageDetection Module ADamage' by Akolyt0r (15.06.2009) REQUIREMENTS: + jAssyst + ADamage by Anitarf + vJass Precompiler (e.g. Jasshelper) ABOUT: This Module is a wrapper to enable you to choose the DDS of your choice, for registering damage events as assist. //! endnovjass // ***************************************************************************************** globals private constant real DAMAGE_THRESHOLD = 1. //wont register any damage below this threshold as assist private boolean ALLIED_DAMAGE_IsAssist = false // ...determines if "allied damage" can be treated as "assist" endglobals private function onDamage takes unit damagedUnit, unit damageSource, real damage, real prevented returns nothing if IsAssistUnitRegistered(damagedUnit) then if damage > DAMAGE_THRESHOLD and((not ALLIED_DAMAGE_IsAssist) or ALLIED_DAMAGE_IsAssist==IsPlayerAlly(GetOwningPlayer(damagedUnit),GetOwningPlayer(damageSource))) then call RegisterAssist(damageSource,damagedUnit) endif endif endfunction private function oninit takes nothing returns nothing call ADamage_AddResponse(ADamage_Response.onDamage) endfunction endlibrary INFO: Map contains libraries (TimerUtils,Table,IDDS*,GetPlayerNameColored*) for WC3 versions below 1.23b and 1.23b and above. If you use 1.23 or below, you have to disable the newer libraries and enable the ones in the 1.23 folder. *: 1.23b versions of IDDS and GetPlayerNameColored are no official releases, yet. |
| 03-01-2009, 11:34 PM | #2 | |
Updated (v1.1) Quote:
will update Testmap later.. EDIT: Do you think i should add a UnRegister method ? ..i cant think of a map which would need it ... |
| 03-03-2009, 05:02 AM | #3 |
ofc you MUST unregister gone units... |
| 03-03-2009, 09:09 AM | #4 |
Wouldn't this count assists from dummy units as well? |
| 03-03-2009, 10:29 AM | #5 | ||
Quote:
when you register them with call AssistUnit.Register(youdDummy) ...of course ...but why would you do that ... Quote:
Hmm maybe you are right ...even though you would only need that functionality when you use this system for normal units aswell ...and not just heroes ...and that would be the most common application for this. Well ..i will add a UnRegister method .... since i use UnitIndexingUtils now ..i dont even need a O(n) operation ...Good Thing. |
| 03-03-2009, 12:43 PM | #6 |
OK added the call AssistUnit.UnRegister(youdDummy)-method ... Currently still leaks the 2 triggerevents (death/damaged)...is it safe to remove them ?! |
| 03-03-2009, 12:48 PM | #7 |
You should use a damage detection system and a generic unit dies event. In fact, players should be able to define themselves what counts as an assist, it shouldn't be hardcoded to only count damage. What constitutes an assist should be up to the users to decide and as such, damage detection shouldn't even be required, it should be an optional module like any other assist condition (although it likely will be used the most). |
| 03-03-2009, 01:25 PM | #8 |
yeah generic death event is no problem ... in a earlier version (not released) ..it was a problem though ( would have needed a O(n) operation)... Hmm you mean i just create a "RegisterAssist(assistingplayer, unit)" method... and then the user can call it whenever he wants to ?... + a additional module which does that automatically for damage events ?! As a matter of fact i dont want to use a damage detection engine ...(i dont need register damage for all units ..only for the ones monitored by this system) .. What i could do however is to make it easy to make the registerdamage-module use a damage-detection engine ... EDIT: Damn ..if i dont want ugly code, AND a external registerdamage-module ...i HAVE to use a damage detection engine .. EDIT2: v1.25 now uses generic death event. I dont think i will do change the damage stuff ... since nearly everybody who wants to use such an Assistance System wants register damage as Assist .... Maybe i will add a basic spell-assist-module ..to register spells which deal no damge (e.g. hex ...) as assists aswell.. BTW: added 2 functions: JASS:function IsAssistUnitRegistered takes unit checkUnit returns boolean function RegisterAssist takes unit assistingUnit, unit victimUnit returns nothing |
| 03-16-2009, 09:03 PM | #9 |
What happens if I register a unit, then unregister it and after some time register it again? Specifically, what happens with the damage detection? It's because of shit like this that I'm saying you should use a damage detection system. |
| 03-16-2009, 09:07 PM | #10 | |
Quote:
Ok i will make another version using a DDS ... But for most uses uses (heroes) this should already be sufficient Also i would need a simple color library (something like GetPlayerColorString(player) ..blabla).... ARGB is really overkill for this, but i havent found any fitting thing in the ressource section .. EDIT: Damn ..just found http://www.wc3c.net/showthread.php?t=101692 |
| 03-16-2009, 09:32 PM | #11 | ||
Quote:
Quote:
|
| 03-16-2009, 10:11 PM | #12 | |
Updated...i use a DDS now ... i chose LLDD, since it minimalism suits my needs perfectly... Quote:
i dont like to do anything like this without a "friend" keyword ....would get pretty ugly. EDIT: btw could you change the thread title please... i changed the name of this system a while ago to "jAssyst" ..(yeah ...even shorter ...assist is a fun word to play around with ;) |
| 03-16-2009, 11:01 PM | #13 | |
Quote:
|
| 03-17-2009, 07:00 AM | #14 | |
Quote:
Well whatever i actually think that an additional library for the messages would not improve this system ... |
| 03-29-2009, 05:20 PM | #15 | |
Quote:
|
