- Joined
- Jul 10, 2007
- Messages
- 6,306
notes
[/quote]
After working with plateaus, I decided it would be interesting to make plateaus act like cliffs do in regards to missing. When a unit on a cliff level attacks a unit on a higher cliff level, the unit has a large**** chance to miss.
This uses accuracy, z level, and 3d distance to determine a unit's chance to hit. A unit at a higher z level has a greater chance to hit while a unit at a lower z level has a much lower chance to hit.
There is always a chance to miss, even if the accuracy is 70000 and the 3D distance is 1 (chance to miss in that situation is probably .0001%).
This, CliffBound, and SlopeSpeed, should drastically increase strategy for wc3.
You will need to tinker with the ACCURACY_RATE and Z_RATE values to get it just right. My current ACCURACY_RATE value might be a bit high... with the mass Z, the yellow unit only hits a little more than 50% of the time using .025.. .0125 would probably work well for ACCURACY_RATE.
For Z_RATE, .8 to .9 seem to work well.
You can also change those values to totally different things and just decrease/increase accuracy norms for the units. Increases will make it more difficult to improve units and decreases will make it easier to increase units. I tried to use norms that were just the unit attack ranges, but norms like 3500-9000 could be used with only 700 attack range for ORPGs or something to make classes more defined (ranged gear adds lots of accuracy, melee gear adds more armor, etc).
All sorts of things can be done with this ; ).
Demo
This is currently unstable. When the firing unit moves, it'll screw up the accuracy stuff. Attack indexing is being worked on so that this will work right 100% of the time. Until then, you can just see the concept of what's going to be done in the demo map ^_-.
I'm going to update the accuracy chance to hit formula to be a bit better. the current one works, but I was never satisfied with it.
[/quote]
After working with plateaus, I decided it would be interesting to make plateaus act like cliffs do in regards to missing. When a unit on a cliff level attacks a unit on a higher cliff level, the unit has a large**** chance to miss.
This uses accuracy, z level, and 3d distance to determine a unit's chance to hit. A unit at a higher z level has a greater chance to hit while a unit at a lower z level has a much lower chance to hit.
There is always a chance to miss, even if the accuracy is 70000 and the 3D distance is 1 (chance to miss in that situation is probably .0001%).
This, CliffBound, and SlopeSpeed, should drastically increase strategy for wc3.
You will need to tinker with the ACCURACY_RATE and Z_RATE values to get it just right. My current ACCURACY_RATE value might be a bit high... with the mass Z, the yellow unit only hits a little more than 50% of the time using .025.. .0125 would probably work well for ACCURACY_RATE.
For Z_RATE, .8 to .9 seem to work well.
You can also change those values to totally different things and just decrease/increase accuracy norms for the units. Increases will make it more difficult to improve units and decreases will make it easier to increase units. I tried to use norms that were just the unit attack ranges, but norms like 3500-9000 could be used with only 700 attack range for ORPGs or something to make classes more defined (ranged gear adds lots of accuracy, melee gear adds more armor, etc).
All sorts of things can be done with this ; ).
JASS:
library RangedAccuracy uses /*
*/UnitIndexer //http://www.hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
globals
//how much 3D distance effects hitting (greater distance is less chance to hit)
//greater value makes distance weight larger
//exponential
private constant real ACCURACY_RATE = .025
//how much change in z effects hitting (positive increases chance, negative decreases)
//greater value makes z weight larger
//exponential
private constant real Z_RATE = .8
endglobals
private function AccuracyFilter takes unit u returns boolean
return true
endfunction
//save, close map, then open map, comment the below, then save
//! external ObjectMerger w3a AIlz RAAn anam "Life Bonus" ansf "(Ranged Accuracy)" Ilif 1 900000 aite 0
/*
struct RangedAccuracy
//accuracy of a given unit (overwritten whenever mass accuracy methods called)
real accuracy
//offset accuracy of a specific unit
real accuracyOffset
//did the unit miss on its last attack?
readonly boolean missed
//sets accuracy for all units of a type
static method setUnitTypeAccuracy takes integer unitType, real accuracy returns nothing
//sets accuracy for all units of a type owned by a player
static method setPlayerUnitTypeAccuracy takes integer unitType, integer playerId, real accuracy returns nothing
*/
struct RangedAccuracy extends array
public real accuracy
public real accuracyOffset
public boolean missedx
private static constant integer NULL_PLAYER = -20
private static hashtable unitTypeAccuracy = InitHashtable()
private static real uaccuracy
private static group ug
private static group mg
private static player op
private static integer muid
private static location loc = Location(0, 0)
private trigger damage
private static boolexpr dc
private static integer removeCount = 0
private static unit array remove
private static timer rem = CreateTimer()
private integer owner
public method operator missed takes nothing returns boolean
return missedx
endmethod
private static method updateAccuracy takes nothing returns nothing
set RangedAccuracy(GetUnitUserData(GetEnumUnit())).accuracy = uaccuracy
endmethod
private static method moveUnit takes nothing returns nothing
if (GetUnitTypeId(GetEnumUnit()) == muid and GetOwningPlayer(GetEnumUnit()) == op) then
call GroupRemoveUnit(ug, GetEnumUnit())
call GroupAddUnit(mg, GetEnumUnit())
endif
endmethod
public static method setUnitTypeAccuracy takes integer unitType, real accuracy returns nothing
call SaveReal(unitTypeAccuracy, NULL_PLAYER, unitType, accuracy)
if (not HaveSavedHandle(unitTypeAccuracy, -NULL_PLAYER, unitType)) then
call SaveGroupHandle(unitTypeAccuracy, -NULL_PLAYER, unitType, CreateGroup())
else
set uaccuracy = accuracy
call ForGroup(LoadGroupHandle(unitTypeAccuracy, -NULL_PLAYER, unitType), function thistype.updateAccuracy)
endif
endmethod
public static method setPlayerUnitTypeAccuracy takes integer unitType, integer playerId, real accuracy returns nothing
call SaveReal(unitTypeAccuracy, playerId, unitType, accuracy)
if (not HaveSavedHandle(unitTypeAccuracy, -playerId, unitType)) then
set ug = LoadGroupHandle(unitTypeAccuracy, -NULL_PLAYER, unitType)
set mg = CreateGroup()
set op = Player(playerId)
set muid = unitType
call SaveGroupHandle(unitTypeAccuracy, -playerId, unitType, mg)
if (ug != null) then
call ForGroup(ug, function thistype.moveUnit)
endif
else
set uaccuracy = accuracy
call ForGroup(LoadGroupHandle(unitTypeAccuracy, -playerId, unitType), function thistype.updateAccuracy)
endif
endmethod
private static method onIndex takes nothing returns boolean
local thistype this
local integer pid
local integer uid
local group g
if (AccuracyFilter(GetIndexedUnit())) then
set this = GetIndexedUnitId()
set pid = GetPlayerId(GetOwningPlayer(GetIndexedUnit()))
set uid = GetUnitTypeId(GetIndexedUnit())
set accuracy = LoadReal(unitTypeAccuracy, pid, uid)
if (accuracy == 0) then
set accuracy = LoadReal(unitTypeAccuracy, NULL_PLAYER, uid)
set g = LoadGroupHandle(unitTypeAccuracy, -NULL_PLAYER, uid)
if (g == null) then
set g = CreateGroup()
call SaveGroupHandle(unitTypeAccuracy, -NULL_PLAYER, uid, g)
endif
else
set g = LoadGroupHandle(unitTypeAccuracy, -pid, uid)
if (g == null) then
set g = CreateGroup()
call SaveGroupHandle(unitTypeAccuracy, -pid, uid, g)
endif
endif
call GroupAddUnit(g, GetIndexedUnit())
set damage = CreateTrigger()
call TriggerRegisterUnitEvent(damage, GetIndexedUnit(), EVENT_UNIT_DAMAGED)
call TriggerAddCondition(damage, dc)
set missedx = false
set g = null
set owner = pid
endif
return false
endmethod
private static method onDeindex takes nothing returns boolean
local thistype this = GetIndexedUnitId()
local integer pid
local integer uid
local group g
if (damage != null) then
set pid = GetPlayerId(GetOwningPlayer(GetIndexedUnit()))
set uid = GetUnitTypeId(GetIndexedUnit())
set g = LoadGroupHandle(unitTypeAccuracy, -pid, uid)
if (g == null) then
set g = LoadGroupHandle(unitTypeAccuracy, -NULL_PLAYER, uid)
endif
call GroupRemoveUnit(g, GetIndexedUnit())
set accuracy = 0
call DestroyTrigger(damage)
set damage = null
set g = null
endif
return false
endmethod
private static method removec takes nothing returns nothing
loop
exitwhen removeCount == 0
set removeCount = removeCount - 1
call UnitRemoveAbility(remove[removeCount], 'RAAn')
endloop
endmethod
private static method onAttack takes nothing returns boolean
local unit attacked = GetTriggerUnit()
local unit attacker = GetEventDamageSource()
local thistype this = GetUnitUserData(attacker)
local real x1
local real x2
local real y1
local real y2
local real xd
local real yd
local real zd
local real z1
local real z2
local real d2
local real d3
local integer pos
local real chanceToHit
local real ac
if (damage != null) then
set x1 = GetUnitX(attacked)
set x2 = GetUnitX(attacker)
set y1 = GetUnitY(attacked)
set y2 = GetUnitY(attacker)
set xd = x2-x1
set yd = y2-y1
set ac = accuracy+accuracyOffset
set pos = 1
call MoveLocation(loc, x1, y1)
set z1 = GetLocationZ(loc)+GetUnitFlyHeight(attacked)
call MoveLocation(loc, x2, y2)
set z2 = GetLocationZ(loc)+GetUnitFlyHeight(attacker)
set zd = z2-z1
set d2 = SquareRoot(xd*xd+yd*yd)
set d3 = SquareRoot(d2*d2+zd*zd)-ac
if (d3 < 0) then
set d3 = 0
endif
if (zd < 0) then
set pos = -1
endif
set chanceToHit = Pow(d3, 1-ACCURACY_RATE)-Pow(zd, Z_RATE)*pos
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, GetPlayerName(GetOwningPlayer(attacker)) + ": " + R2S(Pow(d3, 1-ACCURACY_RATE))+":"+R2S(Pow(zd, Z_RATE)*pos))
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, R2S(ac) + "<" + R2S(chanceToHit))
if (GetRandomReal(0, ac) < chanceToHit) then
if (GetWidgetLife(attacked) <= .405) then
call UnitAddAbility(attacked, 'RAAn')
set remove[removeCount] = attacked
set removeCount = removeCount + 1
call TimerStart(rem, 0, false, function thistype.removec)
endif
call SetWidgetLife(attacked, GetWidgetLife(attacked)+GetEventDamage())
set missedx = true
else
set missedx = false
endif
set attacked = null
set attacker = null
endif
return false
endmethod
private static method onOwnerChange takes nothing returns boolean
local thistype this = GetUnitUserData(GetTriggerUnit())
local integer pid
local integer uid
local group g
if (damage != null) then
set pid = GetPlayerId(GetTriggerPlayer())
set uid = GetUnitTypeId(GetIndexedUnit())
set g = LoadGroupHandle(unitTypeAccuracy, -owner, uid)
if (g == null) then
set g = LoadGroupHandle(unitTypeAccuracy, -NULL_PLAYER, uid)
endif
call GroupRemoveUnit(g, GetIndexedUnit())
set g = LoadGroupHandle(unitTypeAccuracy, -pid, uid)
if (g == null) then
set g = LoadGroupHandle(unitTypeAccuracy, -NULL_PLAYER, uid)
set accuracy = LoadReal(unitTypeAccuracy, NULL_PLAYER, uid)
else
set accuracy = LoadReal(unitTypeAccuracy, pid, uid)
endif
call GroupAddUnit(g, GetTriggerUnit())
set owner = pid
set g = null
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local integer i = 16
local trigger t = CreateTrigger()
call OnUnitIndex(Condition(function thistype.onIndex))
call OnUnitDeindex(Condition(function thistype.onDeindex))
set dc = Condition(function thistype.onAttack)
loop
set i = i - 1
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_CHANGE_OWNER, null)
exitwhen i == 0
endloop
call TriggerAddCondition(t, Condition(function thistype.onOwnerChange))
endmethod
endstruct
endlibrary
Demo
JASS:
struct tester extends array
private static method onInit takes nothing returns nothing
call RangedAccuracy.setUnitTypeAccuracy('earc', 600)
call RangedAccuracy.setUnitTypeAccuracy('ebal', 1150)
endmethod
endstruct
Attachments
Last edited: