Cycle

The Scriptorium

Smart Assembly code templates and tools for on-chain development in Eve Frontier.

Turret Targeting Modes
IntermediateChapter 1 of 415 min read

The Turret API

EVE Frontier turrets are programmable defense systems attached to player structures. Unlike most smart contracts where you decide when to call them, turret targeting code is called by the game engine whenever a potential target enters range. Your job is to write the targeting logic -- the game handles everything else.

The Function Signature

Every turret targeting module must expose a function with this exact signature:

move
public fun get_target_priority_list(
    turret: &Turret,
    owner_character: &Character,
    target_candidate_list: vector<u8>,
    receipt: OnlineReceipt,
): vector<u8>

The game engine calls this function with four arguments. You cannot change the parameter types or add extra parameters -- the interface is fixed.

Parameter 1: turret (&Turret)

An immutable reference to the turret object. Contains the turret's on-chain state (ID, configuration, etc.). You use this primarily for identity verification:

move
assert!(receipt.turret_id() == object::id(turret), 0);

Parameter 2: owner_character (&Character)

The character who owns the turret. You can read properties from this:

move
let owner_tribe = owner_character.tribe();          // u32 tribe ID
let owner_char_id = owner_character.key().item_id(); // u64 character ID

This is how targeting modes decide which targets are "friendly" -- they compare the candidate's tribe with the owner's tribe.

Parameter 3: target_candidate_list (vector<u8>)

A BCS-encoded byte array containing all potential targets in range. You must unpack it:

move
let candidates = turret::unpack_candidate_list(target_candidate_list);

This returns a vector. Each candidate has these accessor functions:

AccessorTypeDescription
item_id()u64Unique item ID of the target
character_id()u32Character ID of the target's pilot
character_tribe()u32Tribe ID of the target
is_aggressor()boolWhether the target has recently attacked
priority_weight()u64Default priority weight from the game engine

Parameter 4: receipt (OnlineReceipt)

The OnlineReceipt is a hot potato -- a value without the drop ability. You must consume it by calling turret::destroy_online_receipt at the end of your function. If you forget, the code will not compile.

move
turret::destroy_online_receipt(receipt, AggressionAuth {});

The second argument to destroy_online_receipt is a typed witness (like XAuth in the storage extension). It proves which targeting module is handling the receipt.

The Hot Potato Pattern

The OnlineReceipt pattern ensures two things:

  • The receipt is consumed: You cannot just ignore it. The Move compiler will reject any code path where an OnlineReceipt goes out of scope without being destroyed.
  • The turret is online: The game engine only creates an OnlineReceipt for turrets that are currently online. By requiring the receipt, the function signature guarantees it cannot be called for offline turrets.
  • move
    // This struct does NOT have drop
    // public struct OnlineReceipt has key {
    //     id: UID,
    //     turret_id: ID,
    // }
    
    // So this would fail to compile:
    public fun bad_targeting(receipt: OnlineReceipt): vector<u8> {
        // ERROR: receipt is not consumed!
        vector::empty()
    }
    
    // You must destroy it:
    public fun good_targeting(receipt: OnlineReceipt): vector<u8> {
        let result = vector::empty();
        turret::destroy_online_receipt(receipt, MyAuth {});
        result
    }

    The Auth Witness

    Each targeting module defines its own auth witness:

    move
    public struct AggressionAuth has drop {}

    This serves the same purpose as XAuth in the storage extension -- it identifies which module is handling the turret. The SSU/turret owner authorizes a specific witness type, and only that module can produce it.

    Return Value

    The function returns vector -- a BCS-encoded list of targets with priorities:

    move
    let mut return_list = vector::empty<turret::ReturnTargetPriorityList>();
    
    // Add targets with priority weights
    return_list.push_back(turret::new_return_target_priority_list(
        candidate.item_id(),
        candidate.priority_weight() + 10000,
    ));
    
    // Encode and return
    let result = bcs::to_bytes(&return_list);

    Each entry in the return list contains:

    • item_id: Which target to shoot (must be from the candidate list)
    • priority_weight: Higher weight = higher priority = shoot first

    Targets not included in the return list are ignored by the turret.

    Putting It Together: Minimal Targeting Module

    Here is the absolute minimum targeting module that shoots everyone:

    move
    module my_turret::shoot_everyone;
    
    use sui::bcs;
    use world::character::Character;
    use world::turret::{Self, Turret, OnlineReceipt};
    
    public struct ShootAllAuth has drop {}
    
    public fun get_target_priority_list(
        turret: &Turret,
        _owner: &Character,
        target_candidate_list: vector<u8>,
        receipt: OnlineReceipt,
    ): vector<u8> {
        assert!(receipt.turret_id() == object::id(turret), 0);
    
        let candidates = turret::unpack_candidate_list(target_candidate_list);
        let mut targets = vector::empty<turret::ReturnTargetPriorityList>();
    
        let mut i = 0;
        while (i < candidates.length()) {
            let c = candidates.borrow(i);
            targets.push_back(
                turret::new_return_target_priority_list(c.item_id(), c.priority_weight()),
            );
            i = i + 1;
        };
    
        let result = bcs::to_bytes(&targets);
        turret::destroy_online_receipt(receipt, ShootAllAuth {});
        result
    }

    This module includes every candidate in the return list with their default priority weight. No filtering, no tribe checks, no aggression checks.

    Key Takeaways

    • Turret targeting modules implement get_target_priority_list with a fixed 4-parameter signature.
    • The game engine calls this function -- you do not call it yourself.
    • target_candidate_list is BCS-encoded and must be unpacked with turret::unpack_candidate_list.
    • OnlineReceipt is a hot potato that must be destroyed with turret::destroy_online_receipt and a typed auth witness.
    • The return value is a BCS-encoded list of (item_id, priority_weight) pairs. Higher weight = higher priority.
    • Targets omitted from the return list are not fired upon.

    Sign in to track your progress.