#&mut self method not mutating self

17 messages · Page 1 of 1 (latest)

raven mauve
#

Here is where I'm calling the above method:

fn try_equip_equipment_system(
    mut events: EventReader<SelectedEquipmentSlot>,
    mut held_item_query: Query<(
        Entity,
        &mut SelectedItem,
        &mut EquipmentSlots,
        &mut Stats,
    )>,
    mut item_query: Query<(
        &EquipmentItem, 
        &StatStick,
        &mut EquippedTo,
    )>,
) {
    for event in events.read() {
        for (
            equipping_entity,
            mut selected_item, 
            mut equipment_slots, 
            mut stats,
        ) in held_item_query.iter_mut() {
            if let Some(held_item_entity) = selected_item.0 {
                let equipment_in_slot = equipment_slots.0.get(event.0).and_then(|slot| slot.entity);
                if equipment_slots.try_equip_item(
                    event.0,
                    held_item_entity,
                    equipping_entity,
                    &mut stats,
                    &mut item_query,
                ) {
                    //equipment_slots.0[event.0].entity = Some(held_item_entity);
                    selected_item.0 = equipment_in_slot;
                } else {
                    println!("Failed to equip item {:?}.", held_item_entity);
                }
            } else {
                let Some(slot_data) = equipment_slots.0.get(event.0) else {
                    println!("Invalid equipment slot");
                    continue;
                };
                
                let Some(equipped_entity) = slot_data.entity else {
                    println!("Equipment slot is empty");
                    continue;
                };
                
                selected_item.0 = Some(equipped_entity);
                equipment_slots.unequip_item(
                    event.0, 
                    equipped_entity,
                    equipping_entity,
                    &mut stats,
                    &mut item_query.transmute_lens::<(&StatStick, &mut EquippedTo)>().query(),
                );
            }
        }
    }
}
#

You can see my commented out line in the block that is called when try_equip_item succeeds. If that is commented out, the value of slot.entity is not assigned. If I uncomment it everything magically works. However I'd like to have code relevant to the process of equipping an item inside of the try_equip_item function.

#

And EquipmentSlots/EquipmentSlot if it's relevant:

#[derive(Debug)]
pub struct EquipmentSlot {
    pub slot_type: SlotType,
    pub entity: Option<Entity>,
    pub is_dead: bool,
}

impl EquipmentSlot {
    pub fn new(slot_type: SlotType) -> EquipmentSlot {
        EquipmentSlot {
            slot_type,
            entity: None,
            is_dead: false,
        }
    }
}


#[derive(Component, Debug)]
pub struct EquipmentSlots(pub Vec::<EquipmentSlot>);
raven mauve
#
    pub fn try_equip_item(
        &mut self,
        index: usize,
        new_item_entity: Entity,
        character_entity: Entity,
        character_stats: &mut Stats,
        item_query: &mut Query<(&EquipmentItem, &StatStick, &mut EquippedTo)>,
    ) -> bool {
        let changed_slot = {
            let Some(slot) = self.0.get_mut(index) else {
                return false;
            };
    
            let Ok((new_equipment_item, new_stat_stick, equipped_to)) = item_query.get(new_item_entity) else {
                println!("Item not found");
                return false;
            };
    
            if equipped_to.0 == Some(character_entity) {
                println!("Item already equipped");
                return false;
            }
    
            if !new_equipment_item.valid_slots.contains(&slot.slot_type) {
                println!("Invalid slot type");
                return false;
            }
    
            if !new_stat_stick.requirements_met(&character_stats) {
                println!("Requirements not met");
                return false;
            }
    
            if slot.is_dead {
                println!("Slot is dead");
                return false;
            }

            let mut slot_clone = slot.clone();
            slot_clone.entity = Some(new_item_entity);
    
            let old_item_entity = slot.entity;
    
            // Equip new item
            let Ok((_, new_stat_stick, mut new_equipped_to)) = item_query.get_mut(new_item_entity) else {
                println!("Item not found");
                return false;
            };
    
            new_equipped_to.0 = Some(character_entity);
            slot.entity = Some(new_item_entity); // This should now work
            character_stats.equip(&new_stat_stick.grants);
    
            // Unequip old item if it exists
            if let Some(old_entity) = old_item_entity {
                self.unequip_item(index, old_entity, character_entity, character_stats, &mut item_query.transmute_lens::<(&StatStick, &mut EquippedTo)>().query());
            }
            Some(slot_clone)
        };
        
        if let Some(slot) = changed_slot {
            self.0[index] = slot.clone();
        }

        return true;
    }

This pattern where I reassign the value in the vec directly at the end of the function seems to work, but why do I have to do this?

rain zenith
#

The presence of a mut in the if let at the top of the function is also suspicious

#

Assigning to the value of a mutable reference does not need the variable holding the reference to be mut

#

If that mut is actually necessarily, then something interesting is going on, and it might be worth investigating

raven mauve
rain zenith
#

Aww, there goes my best guess

vestal shore
#

If I put printlns before and after it can see that the value of slot. entity is mutated. Huzzah. However, once we leave the scope of this function it is no longer set.
Where exactly did you print? Did you try printing immediately before the return?

raven mauve
#

Nah it was like

println!(...)
slot.entity = Some(new_item_entity);
println!(...)
#

just sandwiching the line.

#

But what happens between the line and the return that would switch things up?

            new_equipped_to.0 = Some(character_entity);
            slot.entity = Some(new_item_entity); // This should now work
            character_stats.equip(&new_stat_stick.grants);
    
            // Unequip old item if it exists
            if let Some(old_entity) = old_item_entity {
                self.unequip_item(index, old_entity, character_entity, character_stats, &mut item_query.transmute_lens::<(&StatStick, &mut EquippedTo)>().query());
            }
    
            return true;

slot.entity isn't changed at all. In fact slot isn't touched.

#

Maybe the self.unequip_item call does something?

vestal shore
#

i don't know, but i doubt the return is undoing your work

#

and adding a print for debugging seems easy enough