Jump to content
Mr Elusive

Undefined Variable, but it's already defined.

Recommended Posts

Greetings!
 

Long time lurker, first time poster. I've tried my best to research and resolve this myself, but I'm completely at a loss right now.
 

What am I trying to do?
 

I'm making a sector control mission with AI support. The idea is that the AI will check an array of enemy-held sectors, then assign a waypoint to attack accordingly. For example:

 

  • No enemy sectors? Defend the nearest friendly sector. (BIS_fnc_taskDefend)
  • One enemy sector? Assign a waypoint to attack it.
  • Two or more enemy sectors? Pick one from the closest two and assign a waypoint to attack.


AI units invoke the script using a normal execVM command when they spawn:

[this] execVM "scripts\ai\sectorTacticInfantry.sqf";

What's the issue?
 

The script throws up multiple "undefined variable" errors, but I can't understand why as they've already been defined. For whatever reason, it's like the private variables are not being passed on to the code blocks below.

Here's the script so you can take a look yourselves. I should point out that the "all_sectors" global variable is an array containing the sector module names (S1, S2, S3 etc.) defined in initServer.sqf.

 

systemChat "DEBUG: Starting sector tactic.";

[] spawn {while {true} do
    {
        _unit = (_this select 0);
        _grp = group (_this select 0);
        _grp_side = side (_this select 0);
        _enemy_sectors = [];
        _friendly_sectors = [];

        scopeName "start"; // Update arrays of sector ownership.
        {
            if !((_x getVariable "owner") == _grp_side) then
            {
                [_enemy_sectors, _x] call BIS_fnc_arrayPush;
            };
        } forEach all_sectors;

        {
            if ((_x getVariable "owner") == _grp_side) then
            {
                [_friendly_sectors, _x] call BIS_fnc_arrayPush;
            };
        } forEach all_sectors;
    
    if (count _enemy_sectors == 0) then {breakTo "noEnemySectors"};
    if (count _enemy_sectors == 1) then {breakTo "oneEnemySector"};
    if (count _enemy_sectors > 1) then {breakTo "multipleEnemySectors"};
    
    while {true} do
        {
            scopeName "noEnemySectors"; // There's no enemy sectors.
            _grp = group (_this select 0);
            _grp call CBA_fnc_clearWaypoints;
            _nearest_friendly_sector = [_friendly_sectors, _grp] call BIS_fnc_nearestPosition;
            [_grp, _nearest_friendly_sector] call CBA_fnc_taskDefend;
            breakTo "defendLoop";
        };
    
    while {true} do
        {
            scopeName "oneEnemySectors"; // There's one enemy sector.
            grp call CBA_fnc_clearWaypoints;
            _target_sector = (enemy_sectors select 0);
            [_grp, _target_sector] call CBA_fnc_addWaypoint;
            breakTo "attackLoop";
        };
    
    while {true} do
        {
            scopeName "multipleEnemySectors"; // There's more than one enemy sector.
            _grp call CBA_fnc_clearWaypoints;
            _sortedenemy_sectors = [_enemy_sectors, [], { _unit distance _x }, "ASCEND"] call BIS_fnc_sortBy;
            _closestenemy_sectors = [];
            [_closestenemy_sectors, (_sortedenemy_sectors select 0)] call BIS_fnc_arrayPush;
            [_closestenemy_sectors, (_sortedenemy_sectors select 1)] call BIS_fnc_arrayPush;
            _target_sector = (selectRandom _closestenemy_sectors);
            [_grp, _target_sector] call CBA_fnc_addWaypoint;
            breakTo "attackLoop";
        };
        
    while {true} do
        {
            scopeName "defendLoop"; // Keep defending the point until an enemy takes a sector.
            _enemy_sectors = [];
            {
                if !((_x getVariable "owner") == _grp_side) then
                {
                    [_enemy_sectors, _x] call BIS_fnc_arrayPush;
                };
            } forEach all_sectors;
            if (count _enemy_sectors > 1) then {breakTo "start"};
            sleep 1;
        };
    
    while {true} do
        {
            scopeName "attackLoop"; // Keep attacking the point until it's ours.
            if ((_target_sector getVariable "owner") == _grp_side) then {breakTo "start"};
            sleep 1;
        };
    
    };
};

This is my most desperate hour. Help me, Obi-Wan Kenobi BIS Forums. You're my only hope.

Thanks in advance,
Mr Elusive

Share this post


Link to post
Share on other sites

the variables are not actually defined due to spawn{}. heres an example of what i mean

this will throw an undefined variable error for _unit(inside of spawn{}). the first params ["_passedInUnit"] is referring to [this] that you passed into the sectorTacticInfantry.sqf script, the unit itself which you need to add to the top outside of spawn.

params ["_passedInUnit"];

[] spawn
{
	params ["_unit"];
	hint str _unit;
};

now heres a working example where it knows what _unit is. im passing in [this] just like before into the script(how you called it in your post, no different), im assigning it outside of spawn{}, then i am passing it INTO spawn{} so the script inside of spawn{//script here} knows what it is

params ["_passedInUnit"];

[_passedInUnit] spawn
{
	params ["_unit"];
	hint str _unit;
};

make sense? spawn is not really a part of your .sqf script that you execVM, its more so telling the game to compile/add a new script to the scheduler which is why you have to pass variables into it to use them. i would consider trying to rewrite alot of your script and try to make it more efficient because alot of open loops on alot of AI will cause some lag. try coming up with a method that fires once on a specific event or X seconds and checks all of them at once(i havent really read your script so sorry if that will not achieve what your trying to do). consider creating a function that takes in a unit as a parameter and call it instead

  • Thanks 1

Share this post


Link to post
Share on other sites
1 hour ago, Mr Elusive said:

[] spawn {while {true} do

You are passing empty array [] to spawned script. _this in this case is [], and _this select 0 will be nil as array is empty, if you assign nil to variable in spawned script you get undefined variable complain.

  • Thanks 1

Share this post


Link to post
Share on other sites

Ah, I see what you guys mean. I've not really used the spawn command before. I'll have another go at it.
 

5 hours ago, gokitty1199 said:

i would consider trying to rewrite alot of your script and try to make it more efficient because alot of open loops on alot of AI will cause some lag. try coming up with a method that fires once on a specific event or X seconds and checks all of them at once(i havent really read your script so sorry if that will not achieve what your trying to do). consider creating a function that takes in a unit as a parameter and call it instead


This is a prototype/proof-of-concept just to get it working. Refinement & optimization comes later. My first prototype was running every 30 seconds or so and had no loops. I mean, it worked, but it really wasn't great because the AI only went for the nearest point every single time. Got boring fast unfortunately, so I thought I'd try and improve upon it.

Functions would probably be better, but I haven't learned how to write those yet. I'm not a software developer so some of these programming/scripting concepts are bit alien to me - but I'm learning as I go!

Once again, thank you both for your input. It's most appreciated.
Cheers! 🍺

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×