• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Wurst] SmoothItemPickup

Level 26
Joined
Mar 19, 2008
Messages
3,140
Small snippet for cheating item pickup despite unit inventory being full.
This is done by periodic timer with distance checking that accounts for unit's collision size.
Generates custom event for triggering item pickup.

Allows for specifying arbitrary conditions via SmoothPickupPredicate interface for deciding whether to account for given unit in smooth item pickup process.

Example of usage: ItemRecipe, StackNSplit.
Wurst:
/*
*  SmoothItemPickup v1.0.1.3
*     by Bannar
*
*  Allows for item pickup despite unit inventory being full.
*/
package SmoothItemPickup
import LinkedList
import LinkedListModule
import HashMap
import RegisterEvents
import ClosureTimers
import Orders

tuple eventInfo(unit u, item itm)

var eventState = eventInfo(null, null)
constant eventTrigger = CreateTrigger()

constant conditions = new LinkedList<SmoothPickupPredicate>()
constant looper = CreateTimer()

/** Default pickup range degined in gameplay constants. */
@configurable constant real PICK_UP_RANGE = 150.0

public interface SmoothPickupPredicate
    /** Determinates whether unit can pickup specified item. */
    function canPickup(unit whichUnit, item whichItem) returns boolean

/** Returns unit attempting to pickup event item. */
public function getSmoothItemPickupUnit() returns unit
    return eventState.u

/** Returns item that is being picked up. */
public function getSmoothItemPickupItem() returns item
    return eventState.itm

/** Adds new condition for item to be picked up smoothly. Conditions are aggregated in 'OR' fashion. */
public function addSmoothItemPickupCondition(SmoothPickupPredicate predicate)
    if predicate != null
        conditions.add(predicate)

/** Removes specified condition from predicate list. */
public function removeSmoothItemPickupCondition(SmoothPickupPredicate predicate)
    if predicate != null
        conditions.remove(predicate)

public function registerSmoothItemPickupEvent(code func)
    eventTrigger.addCondition(Condition(func))

class PeriodicData
    use LinkedListModule
    unit picker
    item itm
    real range

    static constant instances = new HashMap<int, PeriodicData>()

    construct(unit u, real range)
        this.picker = u
        this.range = range
        instances.put(u.getHandleId(), this)

    ondestroy
        instances.remove(picker.getHandleId())
        if size == 1
            looper.pause()

    static function get(int index) returns thistype
        thistype result = null
        if instances.has(index)
            result = instances.get(index)
        return result

function fireEvent(eventInfo currState)
    var prevState = eventState
    eventState = currState
    eventTrigger.evaluate()
    eventState = prevState

function test(unit u, item itm, real range) returns boolean
    if u.hasItem(itm)
        return true
    var pos = itm.getPos() - u.getPos()
    // Assumes range is multipled to avoid invoking SquareRoot
    return pos.dot(pos) <= range

function onCallback()
    var iter = PeriodicData.iterator()
    while iter.hasNext()
        var data = iter.next()
        if not data.picker.isAlive() or data.picker.getCurrentOrder() != Orders.move or not data.itm.isPickupable()
            destroy data
        else
            if test(data.picker, data.itm, data.range)
                fireEvent(eventInfo(data.picker, data.itm))
                destroy data
    iter.close()

function onTargetOrder()
    var u = GetTriggerUnit()
    if not u.isInventoryFull() or GetIssuedOrderId() != SpecialOrders.smart
        return

    var itm = GetOrderTargetItem()
    if itm == null or itm.isPowerup()
        return

    var proceed = false
    for condition in conditions
        if condition.canPickup(u, itm)
            proceed = true
            break   
    if not proceed
        return

    var collision = u.getCollisionSize()
    var range = (PICK_UP_RANGE + collision) * (PICK_UP_RANGE + collision)
    if test(u, itm, range)
        // Ensures order is finished before item is picked up.
        // Fixes the issue with unit moving towards the item location, rather than stopping
        nullTimer() ->
            if u.isAlive() and itm.isPickupable()
                fireEvent(eventInfo(u, itm))
    // If unit is not nearby target item, issue artificial move order
    else
        var data = PeriodicData.get(u.getHandleId())
        if data == null
            if PeriodicData.size == 0
                looper.startPeriodic(0.031250000, () -> onCallback())
            data = new PeriodicData(u, range)
        data.itm = itm

        var ipos = itm.getPos()
        var angle = ipos.angleTo(u.getPos())
        var resultPos = ipos + angle.toVec(PICK_UP_RANGE)
        var t = getPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
        t.disable()
        u.issuePointOrderById(Orders.move, resultPos)
        t.enable()

init
    registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, () -> onTargetOrder())
 
Last edited:
Level 26
Joined
Mar 19, 2008
Messages
3,140
Said pull request has been merged. Updating snippet as stated earlier.

Following changes have been merged:

- Periodic callback now validates if item is pickupable before moving to event firing procedure.
- nullTimer callback in onTargetOrder now validates unit and item statuses before firing event.
- Math operations now make use of vec2 and angle objects.
- PICK_UP_RANGE now of type real rather than int.
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,537
Feedback:

- don't name functions "test"
- use vec2.distanceToSquared(vec2) intead of dot product
- consider giving `conditions` a more meaningful name
- consider collapsing all the mainline early-returns into the conditions block
- use ANIMATION_PERIOD
- spelling degined -> defined
- where are docs for the public API?
 
Level 26
Joined
Mar 19, 2008
Messages
3,140
Most of the scripts are still maintained. Also, I'm an advocate for incremental approach i.e. if resource is implemented, tested and most of the review points are addressed, it shall be merged (approved in this case). The merge does not mean the resource cannot be modified. Improvements, fixes and even API changes are all part of what we call maintenance.

Script found here, along with all other item-oriented ones is being used in Island Troll Tribes (ITT) custom map for quite a while now.
 
Top