Aggression Mode
The aggression mode is the most common turret behavior: only shoot targets that have attacked you or your allies. Tribe members are always protected, even if they are aggressors.
Full Source
Here is the complete aggression.move from the turret_monitor package:
/// Mode: AGGRESSION ONLY — shoot anyone who attacks, protect tribe members.
module turret_monitor::aggression;
use sui::bcs;
use turret_monitor::config;
use world::character::Character;
use world::turret::{Self, Turret, OnlineReceipt};
public struct AggressionAuth has drop {}
public fun get_target_priority_list(
turret: &Turret,
owner_character: &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 len = candidates.length();
let owner_tribe = owner_character.tribe();
let owner_char_id = owner_character.key().item_id();
let mut return_list = vector::empty<turret::ReturnTargetPriorityList>();
let mut num_aggressors: u64 = 0;
let mut i: u64 = 0;
while (i < len) {
let candidate = candidates.borrow(i);
let is_aggressor = candidate.is_aggressor();
let is_same_tribe = candidate.character_tribe() == owner_tribe;
let is_owner = (candidate.character_id() as u64) == owner_char_id;
if (is_aggressor) { num_aggressors = num_aggressors + 1; };
// Shoot aggressors only. Protect tribe (even aggressive tribe members excluded).
let excluded = is_owner || is_same_tribe || !is_aggressor;
if (!excluded) {
return_list.push_back(turret::new_return_target_priority_list(
candidate.item_id(), candidate.priority_weight() + 10000,
));
};
i = i + 1;
};
config::emit_alert(
object::id(turret), owner_char_id, owner_tribe,
len, num_aggressors, b"aggression_only",
);
let result = bcs::to_bytes(&return_list);
turret::destroy_online_receipt(receipt, AggressionAuth {});
result
}Line-by-Line Walkthrough
Setup
assert!(receipt.turret_id() == object::id(turret), 0);First safety check: verify that the OnlineReceipt belongs to this turret. This prevents receipt reuse across different turrets.
let candidates = turret::unpack_candidate_list(target_candidate_list);
let len = candidates.length();Decode the BCS-encoded candidate list into a vector of TargetCandidateInfo structs.
let owner_tribe = owner_character.tribe();
let owner_char_id = owner_character.key().item_id();Extract the turret owner's tribe ID and character ID. These are used for comparison against each candidate.
The Targeting Loop
let mut return_list = vector::empty<turret::ReturnTargetPriorityList>();
let mut num_aggressors: u64 = 0;
let mut i: u64 = 0;
while (i < len) {
let candidate = candidates.borrow(i);Initialize an empty return list, an aggressor counter (for alert events), and a loop index. SUI Move uses while loops -- there is no for loop syntax.
let is_aggressor = candidate.is_aggressor();
let is_same_tribe = candidate.character_tribe() == owner_tribe;
let is_owner = (candidate.character_id() as u64) == owner_char_id;For each candidate, determine three boolean properties:
- is_aggressor: Has this target recently attacked the turret owner or allies?
- is_same_tribe: Does this target belong to the owner's tribe?
- is_owner: Is this the turret owner themselves? (Cannot shoot yourself.)
Note the cast (candidate.character_id() as u64) -- character_id() returns u32 but item_id() returns u64, so a cast is needed for comparison.
if (is_aggressor) { num_aggressors = num_aggressors + 1; };Count aggressors for the alert event (emitted after the loop).
The Exclusion Logic
let excluded = is_owner || is_same_tribe || !is_aggressor;This is the core targeting decision. A target is excluded (not shot) if ANY of these are true:
is_owner-- never shoot the turret owneris_same_tribe-- never shoot tribe members (even aggressive ones)!is_aggressor-- only shoot aggressors
The order of these checks does not matter logically (they are OR-ed), but reading it as "exclude the owner, exclude the tribe, exclude non-aggressors" makes the intent clear.
Adding to the Return List
if (!excluded) {
return_list.push_back(turret::new_return_target_priority_list(
candidate.item_id(), candidate.priority_weight() + 10000,
));
};If the target passes all checks (is an aggressor, is not the owner, is not in the tribe), add it to the return list with a boosted priority weight (+ 10000). The weight boost ensures aggressors are prioritized over the game engine's default ordering.
The Alert Event
config::emit_alert(
object::id(turret), owner_char_id, owner_tribe,
len, num_aggressors, b"aggression_only",
);After processing all candidates, emit an on-chain event if there are aggressors. This is handled by the shared config module:
// From turret_monitor::config
public fun emit_alert(
turret_id: ID,
owner_character_id: u64,
owner_tribe_id: u32,
num_targets: u64,
num_aggressors: u64,
mode: vector<u8>,
) {
if (num_aggressors > 0) {
event::emit(TurretAlertEvent {
turret_id, owner_character_id, owner_tribe_id,
num_targets, num_aggressors, mode,
});
};
}Events are only emitted when aggressors are present. Off-chain services can subscribe to TurretAlertEvent to send notifications, log attacks, or trigger automated responses.
Cleanup
let result = bcs::to_bytes(&return_list);
turret::destroy_online_receipt(receipt, AggressionAuth {});
resultEncode the return list as BCS bytes, destroy the hot potato receipt with the AggressionAuth witness, and return the encoded result.
Behavior Summary
Given these candidates:
| Target | Tribe | Aggressor? | Result |
| Owner | Same | - | Excluded (is_owner) |
| Tribe ally | Same | No | Excluded (is_same_tribe) |
| Tribe ally | Same | Yes | Excluded (is_same_tribe) |
| Neutral player | Different | No | Excluded (!is_aggressor) |
| Hostile attacker | Different | Yes | TARGETED |
The aggression mode is purely defensive -- it only fires at non-tribe aggressors.
The Priority Weight System
The priority_weight value determines the order in which the turret engages targets. Higher weight = engaged first.
candidate.priority_weight() + 10000The base weight comes from the game engine and typically factors in distance, target size, and other game mechanics. Adding 10000 to aggressors ensures they are always high-priority targets.
If you wanted to add sub-prioritization (e.g., prioritize closer aggressors), you could use the base weight without the flat bonus, or add a variable bonus.
Key Takeaways
- Aggression mode targets only non-tribe aggressors. Tribe members are always protected.
- The exclusion logic is a single boolean expression:
is_owner || is_same_tribe || !is_aggressor. - Priority weight
+ 10000ensures aggressors are targeted before any default ordering. TurretAlertEventis emitted on-chain when aggressors are present, enabling off-chain notifications.- The hot potato receipt is destroyed with
AggressionAuth {}-- the module-specific witness type.
Sign in to track your progress.