| 11-05-2009, 08:45 PM | #1 |
Well, I already made GetUnitCost. And I am planning to make a very nice system, but for that I need GetUnitDamage. And for GetUnitDamage I need GetUnitAttackType and GetUnitArmorType. But all the systems have a similar structure, I have to do the same safety checkings, got a dummy owner etc. Shall I put all of those into one big script and call it UnitData? Or would it cause too much code bloat (would have like 800 lines maybe). GetUnitDamage is actually finished and works very very fine, but it has some bad limitations listed in the contra list. I decided to remake the system, but for that I need the other systems. JASS:library GetUnitDamage initializer init requires Table //=========================================================================== // Information: //============== // // Getting a units damage is complicated and never 100% accurrate. This is only system // I know that is able to detect a units damage. And knowing a units damage can be very // very useful. My example is the Manrate system (coming soon). A system that allows dynamic // attack cooldowns. But you can't increase a units attack speed by more than 500% and you'd // need some research to get the right values, so I have to simulate attacks. // //=========================================================================== // Implementation: // =============== // // 1. Make a new trigger and convert it to custom text. // 2. Insert the whole code. // 3. Make sure you have the JassNewGen: [url]http://www.wc3c.net/showthread.php?t=90999[/url] // 4. Download Table and implement it (like the GetUnitDamage system): // [url]http://www.wc3c.net/showthread.php?t=101246[/url] // 5. Import the 'GUDD' dummy // 6. Import the 'GUDA' ability // 7. Make the rawcodes of the dummy/ability fit to the constants. // 8. Filter units you don't want to get the damage of with the UserFilter. // 9. Save the map // //=========================================================================== // FAQ: // === // // 1. How does this system work accurately? // Each time a new unit type enters the map, several clones are made of // it in the upper right corner that attack a dummy unit with no armor. // The damage is saved to a struct. If all clones have attacked, the struct // can be used and the units are killed. Of course everything is invisible, // nobody will notice. // // 2. How does this handle heroes? Their damages depend on their levels. // This system doesn't support heroes at all. // // 3. What if 'new' units have a damage bonus? // The clones are just units of the same type of the 'new' unit. // So it will just detect the basic damage of a unit, if you didn't // add the bonuses by an aura. // //=========================================================================== // Functions: //=========== // // GetUnitDamage(unit,type*) : Returns the damage of a unit // GetUnitdamage_Debug() : Use this if the system doesn't work correctly. It will put out portential bugs. // // * type can be DAMAGE_ITEM_MAX_DAMAGE, DAMAGE_ITEM_MIN_DAMAGE or DAMAGE_ITEM_AVERAGE_DAMAGE // //=========================================================================== // Pros: // ===== // // - This is the only system that can get a units damage accurately. And that // can be very useful // //=========================================================================== // Contras: // ======= // // - This will increase the handle id by number of different unit types * ATTACK_COUNT // - The system only works correctly if a unit of the type has been longer than cooldown // of the unit / 5 seconds on the map. // ( Example: My Unit has a cooldown of 1.00. If a unit of that types enters the map the first // time, the system will register the damage of its unit type after 0.2 seconds. You can avoid that // by preplacing the unit) // - This uses some performance each time a unit enters the map or is damaged. // - Doesn't support heroes (can be added if highly desired) // - This can't handle units with attacks that only allow specific targets // (like only structures) // //=========================================================================== globals constant integer DAMAGE_ITEM_MAX_DAMAGE = 0 constant integer DAMAGE_ITEM_MIN_DAMAGE = 1 constant integer DAMAGE_ITEM_AVERAGE_DAMAGE = 2 private constant integer PLAYER_ID = 12 private constant integer DUMMY_ID = 'GUDD' private constant integer ABILITY_ID = 'GUDA' // Higher number = more accurate, slower, use more handle IDs private constant integer ATTACK_COUNT = 25 private constant real MAX_TOLERANCE_VALUE = 2.5 endglobals private function UserFilter takes unit u returns boolean return (GetUnitAbilityLevel(u,'Aloc') == 0) endfunction // DO NOT EDIT BELOW THIS LINE globals private trigger unitEnters = CreateTrigger() private trigger unitDamaged = CreateTrigger() private Table UnitDamage private Table GhostUnitDamage private real maxX private real maxY private unit dummy = null private player dummyOwner = Player(PLAYER_ID) endglobals struct UnitDamageStruct integer instance = 0 real averageDamage = 0. real maxDamage = 0. real minDamage = 0. endstruct public function Debug takes nothing returns nothing // Has some flaws, but it's unimportant: Only for debugging purpose. local group g = CreateGroup() local unit u = null local integer count = 0 if ATTACK_COUNT == 0 then call BJDebugMsg("GetUnitDamage Debug: ATTACK_COUNT is set to 0.") endif if GetPlayerId(dummyOwner) < 12 then call BJDebugMsg("GetUnitDamage Debug: Invalid dummyOwner number. Please chose a number below 12.") endif call GroupEnumUnitsOfPlayer(g,dummyOwner,null) loop set u = FirstOfGroup(g) exitwhen u == null if GetUnitTypeId(u) == DUMMY_ID then set count = count + 1 call BJDebugMsg("GetUnitDamage Debug: Got too many dummy units. Please report this bug.") call GroupClear(g) if GetUnitName(u) != "GUDD (GetUnitDamageDummy)" then call BJDebugMsg("GetUnitDamage Debug: You didn't import the dummy unit correctly.") endif endif call GroupRemoveUnit(g,u) endloop if count == 0 then call BJDebugMsg("GetUnitDamage Debug: Didn't find dummy unit.") endif call DestroyGroup(g) endfunction function GetUnitDamage takes unit u, integer damageItem returns real local UnitDamageStruct UD debug if IsUnitType(u,UNIT_TYPE_HERO) then debug call BJDebugMsg("GetUnitDamage: Used hero.") debug return 0. debug elseif u == null then debug call BJDebugMsg("GetUnitDamage: Used null unit.") debug return 0. debug endif set UD = UnitDamage[GetUnitTypeId(u)] if UD == null then debug call BJDebugMsg("GetUnitDamage: Tried to get damage that's not registered yet.") endif //call BJDebugMsg("ID of struct: "+I2S(UD)) if damageItem == DAMAGE_ITEM_MAX_DAMAGE then return UD.maxDamage elseif damageItem == DAMAGE_ITEM_MIN_DAMAGE then return UD.minDamage elseif damageItem == DAMAGE_ITEM_AVERAGE_DAMAGE then return UD.averageDamage else debug call BJDebugMsg("GetUnitDamage: Used invalid damage item") return 0. endif endfunction private function DefineUnitTypeDamage takes integer ID returns nothing local UnitDamageStruct UD = UnitDamage.create() local integer i = 0 local unit u set UD.minDamage = 10000000. set GhostUnitDamage[ID] = UD loop set i = i + 1 exitwhen i > ATTACK_COUNT set u = CreateUnit(dummyOwner,ID,maxX,maxY,0.) call SetUnitPathing(u,false) call UnitAddAbility(u,ABILITY_ID) call IssueTargetOrder(u,"attack",dummy) call ShowUnit(u,false) call UnitApplyTimedLife(u,'BTLF',MAX_TOLERANCE_VALUE) endloop endfunction private function returnFlag takes nothing returns boolean return UserFilter(GetFilterUnit()) endfunction private function onEnter takes nothing returns nothing if GetOwningPlayer(GetTriggerUnit()) != dummyOwner then call TriggerRegisterUnitEvent(unitDamaged,GetTriggerUnit(),EVENT_UNIT_DAMAGED) if UnitDamage.exists(GetUnitTypeId(GetTriggerUnit())) == false then call DefineUnitTypeDamage(GetUnitTypeId(GetTriggerUnit())) endif endif endfunction // Well, I COULD require a damage system, but I have to use all units that // enter the map or are in the map in the beginning anyways. // Just a few lines of code. private function onDamage takes nothing returns nothing local integer ID = GetUnitTypeId(GetEventDamageSource()) local real damage local UnitDamageStruct UD if GetOwningPlayer(GetEventDamageSource()) == dummyOwner then call RemoveUnit(GetEventDamageSource()) if UnitDamage.exists(ID) == false then // We keep the Ghost Struct in reference until all of the mirror dummies // attack the damage-getter dummy. set damage = GetEventDamage() set UD = GhostUnitDamage[ID] if damage > UD.maxDamage then set UD.maxDamage = damage endif if damage < UD.minDamage then set UD.minDamage = damage endif set UD.averageDamage = UD.averageDamage + damage set UD.instance = UD.instance + 1 if UD.instance == ATTACK_COUNT then // Attack thing finished. Now UD is a 'real' registered struct. set UnitDamage[ID] = UD call GhostUnitDamage.flush(ID) set UD.averageDamage = UD.averageDamage / I2R(ATTACK_COUNT) endif endif endif endfunction private function init takes nothing returns nothing local integer index = 0 local group g = CreateGroup() // Catch also initial units. local unit u local boolexpr condition = Condition(function returnFlag) local UnitDamageStruct uc set UnitDamage = Table.create() set GhostUnitDamage = Table.create() // ========================================================================== set maxX = GetRectMaxX(bj_mapInitialPlayableArea)-500 set maxY = GetRectMaxY(bj_mapInitialPlayableArea)-500 set dummy = CreateUnit(dummyOwner,DUMMY_ID,maxX,maxY,270) // ========================================================================== call TriggerRegisterEnterRectSimple(unitEnters,bj_mapInitialPlayableArea) call TriggerAddAction(unitEnters,function onEnter) call TriggerAddAction(unitDamaged,function onDamage) // ========================================================================== call GroupEnumUnitsInRect(g,bj_mapInitialPlayableArea,condition) loop set u = FirstOfGroup(g) exitwhen u == null if UnitDamage.exists(GetUnitTypeId(u)) == false then call DefineUnitTypeDamage(GetUnitTypeId(u)) endif call TriggerRegisterUnitEvent(unitDamaged,u,EVENT_UNIT_DAMAGED) call GroupRemoveUnit(g,u) endloop call DestroyBoolExpr(condition) call DestroyGroup(g) //call BJDebugMsg("init function successful") endfunction endlibrary |
| 11-06-2009, 06:48 AM | #2 |
this can be done faster with gmsi at the price that you've to execute the script everytime you changed something in the oe |
| 11-06-2009, 11:00 AM | #3 |
Im actually just trying to learn how to do that stuff. Is there any good tutorial about how to do that for lazy guys like me who would happily skip this read GMSI manual part? |
| 11-06-2009, 11:34 AM | #4 | |
Quote:
Don't know if there is any tutorial flying around I've learned it via TnE |
