| 11-23-2009, 03:19 AM | #1 |
So, just wondering if you guys knew of any efficient way to calculate a players APM. I have some ideas on how to go about this, but just wondering what you guys thought |
| 11-23-2009, 05:18 AM | #2 |
Efficient? Well, I wouldn't see any means of efficiency. There is no way to get the exact amount of APM, however. For example, if a player has hotkeyed a unit/hero/group to "1" and constantly spam the button "1", it will not pick up the action if they are already highlighted or selected. Lots of other things that cannot be calculated. |
| 11-23-2009, 08:46 AM | #3 |
any order = +1 to APM works well in AOS only. |
| 11-23-2009, 11:39 AM | #4 |
Any order is only one type of action. What about when a player selects a unit? What about when a player uses a target-skill but cancels it? How about when a player presses the F# keys? Some of these can be detected, some can't. All of these cannot be added by simply "any order +1" method. |
| 11-23-2009, 12:09 PM | #5 |
Calculate what you like, order is most effective way. |
| 11-23-2009, 08:21 PM | #6 |
I'm not completely sure what APM is but I am guessing it's the number of actions per minute with a unit?? Anyways, you could just hook all that deal with unit actions. |
| 11-23-2009, 08:43 PM | #7 |
No, not with a unit. That would be APMU. This is overall actions per minute a player makes. A typical professional gamer would average around 300 APM. And really crazy ones get around 400 APM. If you are to make an APM calculator on WC3, you can never make a perfect one because WC3 lacks the functions to be able to do so. You can get damn near accurate, but not perfect. There are many programs out there that can display your APM while playing any game. WC3's editor lacks the ability to recognize every keystroke or selection. There are a few other things that cannot be calculated by WC3's editor, but like I said, it can come damn near close to being perfectly correct. |
| 11-24-2009, 09:45 AM | #8 |
just detect all select and order events by the player, since these are the only 'actions' he can do (but maybe im missing some?). [F1]/[hotkey[group/non-group]] selection -> selection event whenever there is a selection/order event mark some flag as 'true' and start a 0.0 timer and mark a +1 to actions. if another selection event occurs during this '0.0 interval', it is a group selection event and do not add (+1) to actions for these selection events. (u should probably add in some stuff to compensate for 'order changes' - since scripts can issue orders during an order event - issued by scripts. also, see below). only other problem is to distinguish between player and script-issued orders. maybe you can hook all order natives to set a flag to 'true' when an order is issued by the natives (set it to false afterwards); refer to this flag to determine if an order is script-issued. but maybe im missing some things. edit: dont think u can detect selection for destructables and items, but these might not be so relevant. actually, maybe you should hook selection natives, too. ull need to have flag (boolean) and timer 'sets' on-per-player. for 'action packets', which involve one or more action events depending on whether or not the action is a group action, you can prob generalize all of a player's actions and account for them with only 1 boolean for the player since a player can execute only one action at a time. however, script-issued order flags will prob have to be 1 per native/bj. edit2: but all this only counts/logs actions. ull need something to 'monitor it through time'. ull prob need to run a timer for 60.000 seconds (1 min) each action that detracts 1 action count from the total APM count on-elapse. there are prob other ways to do this, but they dont seem 'nice'; but this method will use one timer per action, and there might be many actions (just for a single plyaer, consider multiple). the natives/bjs involving selection and ordering are many but they can be reduced to a smaller amount natives (bjs... 'inlined'...). for selection, there are (inactive) deselection events. ex: u have a footman selected and u select another footman (or even a group of other footmen). the previously selected footman gets deselected. this will register a deselection event but it is not actively done by the player; it cant be counted to APM. but this is handled by the 'event packet' check (above). deselection events dont happen when units die (i remember testing this sometime, or maybe im just imagining) edit3: i wanted to try making it and this is what i have so far (read 'current build info' comment): note: the 0.00 duration timer works for 'action packets' but it seems to 'lag' coz not all action events are being registered. JASS:library APM initializer Initializer uses TimerUtils // Actions by players: // - selection (hotkey or no) // - active deselection - that is, deselection not done by selecting another object. // - order issue // Current build info: // - does not implement player- and script-action differentiation. All actions are assumed to be done by // player. // - works only for one player. just macro/struct it or something later when its 'all clear'. //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// // -- CONFIGURATION -- // -- CONFIGURATION END -- //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII// //-----------------------------------------------------------------------------------------------------------------// // -- Script Action Monitors -- globals private boolean notScriptIssuedAction = true // Indicates if an action ((de)selection/order-issuing) is done by a script. // Functions below run only for action done by players and they will ignore actions by scripts. // (This should probably be 'one-per-native' (see comments somewhere)). endglobals private function OnSelectUnit takes unit whichUnit, boolean flag returns nothing endfunction //-----------------------------------------------------------------------------------------------------------------// // -- Action Events -- globals private timer eventPacketTim // Timer used to 'time' action event packets. // Event packets elapse in 0.000 seconds, during which it is impossible both to execute another action. // All events that occur in an event packet correspond to only one action. If multiple events occur in an event // packet, then a group action is involved. private boolean packetNotElapsing = true // Indicates if a player action event is the first in an event packet. Since any single event packet corresponds to // a single action, actions can be logged on the initiation of an event packet. // Any subsequent events in an event packet are ignored. private integer apm = 0 // Number of actions (currently) done by a player. // Individual action packets expire in a minute (60 seconds). Actions stop counting to APM when they // expire. endglobals function GetAPM takes nothing returns integer return apm endfunction private function OnPacketElapse takes nothing returns nothing // Un-log event packet. set packetNotElapsing = true endfunction private function IsRelevantAction takes nothing returns boolean return notScriptIssuedAction and packetNotElapsing endfunction private function OnPacketExpire takes nothing returns nothing // Expire action; detract from action count. set apm = apm - 1 call ReleaseTimer(GetExpiredTimer()) endfunction private function OnAction takes nothing returns nothing local timer tim = NewTimer() // Log action. set apm = apm + 1 // This action won't count to APM in a minute (60 seconds). Initiate this-action expire. call TimerStart(tim, 60.000, false, function OnPacketExpire) // Log event packet. set packetNotElapsing = false // Initiate packet elapse. call TimerStart(eventPacketTim, 0.000, false, function OnPacketElapse) set tim = null endfunction //-----------------------------------------------------------------------------------------------------------------// // -- Scope Initializer -- hook SelectUnit OnSelectUnit /*hook IssueBuildOrder OnIssueBuildOrder hook IssueBuildOrderById OnIssueBuildOrderById hook IssueImmediateOrder OnIssueImmediateOrder hook IssueImmediateOrderById OnIssueImmediateOrderById hook IssueInstantPointOrder OnIssueInstantPointOrder hook IssueInstantPointOrderById OnIssueInstantPointOrderById hook IssueInstantTargetOrder OnIssueInstantTargetOrder hook IssueInstantTargetOrderById OnIssueInstantTargetOrderById hook IssueNeutralImmediateOrder OnIssueNeutralImmediateOrder hook IssueNeutralImmediateOrderById OnIssueNeutralImmediateOrderById hook IssueNeutralPointOrder OnIssueNeutralPointOrder hook IssueNeutralPointOrderById OnIssueNeutralPointOrderById hook IssueNeutralTargetOrder OnIssueNeutralTargetOrder hook IssueNeutralTargetOrderById OnIssueNeutralTargetOrderById hook IssuePointOrder OnIssuePointOrder hook IssuePointOrderById OnIssuePointOrderById hook IssuePointOrderByIdLoc OnIssuePointOrderByIdLoc hook IssuePointOrderLoc OnIssuePointOrderLoc hook IssueTargetOrder OnIssueTargetOrder hook IssueTargetOrderById OnIssueTargetOrderById*/ private function Initializer takes nothing returns nothing local trigger trig set eventPacketTim = CreateTimer() set trig = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SELECTED) call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DESELECTED) call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ISSUED_ORDER) call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER) call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER) call TriggerAddCondition(trig, Condition(function IsRelevantAction)) call TriggerAddAction(trig, function OnAction) set trig = null endfunction endlibrary to test, just load a map with units you can play with, import these triggers and requirements (timerutils), and run the map. JASS:scope Test initializer Initializer private function DisplayAPM takes nothing returns nothing call BJDebugMsg("APM: " + I2S(GetAPM())) endfunction private function Initializer takes nothing returns nothing call TimerStart(CreateTimer(), 1.000, true, function DisplayAPM) endfunction endscope |
| 11-24-2009, 01:37 PM | #9 |
seems that selection events are 'laggy'. they dont get registered if followed quickly enough by another selection event (even if the units in the first event respond by saying something). EDIT: actually, this is wrong. problem's with the 0.0 duration timer. it seems that a 0.0 duration timer neither really 0.0 nor very short. it is possible for a player order an order within the 0.00 interval... |
