moving units: AI and human

Joined
Jul 31, 2002
Messages
705
edit: figured it out, here's an example on how to do it:

Spoiler :
Code:
local trackarg1 = nil;
local trackarg2 = nil;
local trackarg3 = nil;
local trackarg4 = nil;
local moveai = 0;

function dirtyunit ()

	--print("GAMETURN: " .. Game.GetGameTurn() );
	if ( moveai == 0 ) then return; end
	if ( trackarg1 == nil and trackarg2 == nil and trackarg3 == nil and trackarg4 == nil ) then return; end

	local player = Players[ trackarg1 ];
	local unit = player:GetUnitByID( trackarg2 );

	if ( player:IsTurnActive() and unit:GetMoves() > 0 ) then
		local hx, hy  = ToGridFromHex( arg3, arg4 );
		local plot = Map.GetPlot(hx, hy);
		unit:PopMission();
		unit:PushMission(0, trackarg3, trackarg4, 0, 0, 1, 0, plot, unit);
	end
end
Events.SerialEventUnitInfoDirty.Add ( dirtyunit );

function utrack (arg1, arg2, arg3, arg4)

	trackarg1 = arg1;
	trackarg2 = arg2;
	trackarg3 = arg3;
	trackarg4 = arg4;

	if (arg1 == nil) then
		print("Usage: utrack(CIVNUM, UNITID)");
		print("Available units:");

		for lnum = 0, GameDefines.MAX_CIV_PLAYERS-1, 1 do
			local player = Players[ lnum ];
			if( player:IsAlive() and not player:IsMinorCiv() ) then
				print("CIVNUM: " .. lnum .. "\t(" .. civstring(player) .. ")");
				print("Units:");
				for unit in player:Units() do
					print("UNITID: " .. unit:GetID() .. "\t( " .. unit:GetName() .. ", x: " .. unit:GetX() .. ", y: " .. unit:GetY() );
				end
			end
		end
	else
		local player = Players[ arg1 ];
		local unit = player:GetUnitByID( arg2 );

		if ( player:IsHuman() ) then
			local hx, hy  = ToGridFromHex( trackarg3, trackarg4 );
			local plot = Map.GetPlot(hx, hy);
			unit:PopMission();
			unit:PushMission(0, trackarg3, trackarg4, 0, 0, 1, 0, plot, unit);
		else
			moveai = 1;
		end
	end
end

function civstring (player)

	local civinfo;
	if ( not player:IsMinorCiv() ) then civinfo = GameInfo.Civilizations[ player:GetCivilizationType() ];
	else civinfo = GameInfo.MinorCivilizations[ player:GetMinorCivType() ];
	end
	local string = "string";
	if (civinfo ~= nil) then
		string = Locale.ConvertTextKey( civinfo.Description);
	end
	return string;
end
use utrack() to get the civ, unit, and use utrack(civ, unit, x, y) to send the unit somewhere. it's not perfect, can't get the AI to move on the very first turn, but otherwise seems to work okay:)
 
Thanks, I am going to use it in my barbarians mod to spawn the first settlers in different locations.

Ok, seems this is to give a move order... will need to investigate how to place directly in certain spots.

It is probably this one: void unit:setXY(int iX, int iY, boolean bGroup = false, boolean bUpdate = true, boolean bShow = false, bCheckPlotVisible = false);

I should check that the tile is land, passable... Also to see if there is no other unit in that time... Any more you can think of?

boolean plot:isWater();
boolean plot:isImpassable();
boolean plot:isBarbarian();

Maybe this one makes them move away from other civs? boolean plot:isOwned();
 
the order won't be accepted if it's not reachable, not revealed to player (at least for humans), etc. the AI takes care of the pathfinding. asides from if a unit is there, but thats plot:GetUnits() to check iirc

in my sig theres a link to ICSing AI mod that controls settler movement, plenty of examples there you can build off of to fine tune your control

for plot:isOwned(), a quick duel size map with the tuner/luaconsole and type in

Map.GetPlot( x, y ):IsOwned();

and it seems that it returns true if a tile is under cultural influence only
 
Ok, I have made my part using your utrack function, cannot test it yet. Maybe you can spot if I made something really bad as I am new to LUA. I also have seen your ICSing AI mod, looking into the code I spotted something that could lead to problems (again I could be wrong).

You are using:

if ( string.find( unit:GetName(), 'Settler') ) then

Maybe it would be better to use the unit type as the name probably refers to the unit name that can be changed by the player, not probably.

unit:getUnitType();

Here is my code for finding random valid plots for settlers, I will test it this afternoon:

Code:
function validplot (civ,unitset,arg1,arg2)
  
    valid = 0;
    iterations =0;
	local hx, hy  = ToGridFromHex( arg1, arg2 );

	--not owned
	if ( not Map.GetPlot(hx, hy):IsOwned() ) then
	  --is passable
	  if ( not Map.GetPlot(hx, hy):isImpassable() ) then
		--is land
		if ( not Map.GetPlot(hx, hy):isWater() ) then
			--is not barbarian
			if ( not Map.GetPlot(hx, hy):isBarbarian() ) then
				valid=1;
			end
		end
	  end
	end

	if (valid==0 and iterations<10) then
	    --choose another random spot
		local iW, iH = Map.GetGridSize();
		--validplot (rand)
		posx = math.random (1,iW);
		posy = math.random (1,iH);
		validplot (posx,posy);
		validplot (civ,unitset,posx,posy);
		iterations=iterations+1;
	else
		--order move unit			
		if (valid==1) then
		  utrack (civ, unitset, arg1, arg2);
		end
	end
end


function movesettlers ()
	for lnum = 0, GameDefines.MAX_CIV_PLAYERS-1, 1 do
            local player = Players[ lnum ];
            if( player:IsAlive() and not player:IsMinorCiv() and player:IsHuman() ) then                
                for unit in player:Units() do
                    --print("UNITID: " .. unit:GetID() .. "\t( " .. unit:GetName() .. ", x: " .. unit:GetX() .. ", y: " .. unit:GetY() );
					if (string.find( unit:getUnitType(), 'SETTLER') ) then
						local iW, iH = Map.GetGridSize();
						--validplot (rand)
						posx = math.random (1,iW);
						posy = math.random (1,iH);
						validplot (lnum,unit:GetID(),posx,posy);
					end
                end
            end
    end		
end
Events.SequenceGameInitComplete.Add ( movesettlers );

Code:
Changed this part of your function:

            --unit:PopMission();
            --unit:PushMission(0, trackarg3, trackarg4, 0, 0, 1, 0, plot, unit);
			--move the unit
			void unit:setXY(hx, hy, false, true, false, false);
 
i didnt know we could change unit names :D but yeah i should come up with an adaptable function that can build up unit/building/etc indexes instead of relying on hardcoded numbers and names

a quick look at what you posted...

Map.GetPlot(hx, hy):isBarbarian()
i dont know how this can ever return true because camps/units doesnt seem to do it. there's a example in the vanilla files using plot:GetNumUnits() that could let you check if a barb is present or not

in the validplot if block, validplot (posx,posy) is going to give you errors i think

unit:SetXY(x, y) should be sufficient to instantly teleport the settler unless one of those false values is used to check plot legibility? and can you have it start the line with void and not get errors?
 
Thanks for the comments and spotting my errors, will check it this afternoon.

BTW you are safe with getting settler by name, I think I read that you can only rename units when they promote, guess the settler never gets renamed ;)
 
Hum, do you know what I am doing wrong

I get this error when calling utrack:
Runtime Error: [string "_cmdr = {utrack()}"]:1: attempt to call global 'utrack' (a nil value)
stack traceback:
[string "_cmdr = {utrack()}"]:1: in main chunk

I have added the line to Ingame.xml, the name of the lua file:

<LuaContext FileName="moveauto2" ID="moveauto2" Hidden="True" />
 
Yep, seems strange. I added a test function to see that the lua was loading up fine. Just copy and paste your code.

Spoiler :
Code:
local trackarg1 = nil;
local trackarg2 = nil;
local trackarg3 = nil;
local trackarg4 = nil;
local moveai = 0;

function realstart ()
                print("Really really just started a game!");
end
Events.SequenceGameInitComplete.Add ( realstart );

function dirtyunit ()

	--print("GAMETURN: " .. Game.GetGameTurn() );
	if ( moveai == 0 ) then return; end
	if ( trackarg1 == nil and trackarg2 == nil and trackarg3 == nil and trackarg4 == nil ) then return; end

	local player = Players[ trackarg1 ];
	local unit = player:GetUnitByID( trackarg2 );

	if ( player:IsTurnActive() and unit:GetMoves() > 0 ) then
		local hx, hy  = ToGridFromHex( arg3, arg4 );
		local plot = Map.GetPlot(hx, hy);
		unit:PopMission();
		unit:PushMission(0, trackarg3, trackarg4, 0, 0, 1, 0, plot, unit);
	end
end
Events.SerialEventUnitInfoDirty.Add ( dirtyunit );

function utrack (arg1, arg2, arg3, arg4)

	trackarg1 = arg1;
	trackarg2 = arg2;
	trackarg3 = arg3;
	trackarg4 = arg4;

	if (arg1 == nil) then
		print("Usage: utrack(CIVNUM, UNITID)");
		print("Available units:");

		for lnum = 0, GameDefines.MAX_CIV_PLAYERS-1, 1 do
			local player = Players[ lnum ];
			if( player:IsAlive() and not player:IsMinorCiv() ) then
				print("CIVNUM: " .. lnum .. "\t(" .. civstring(player) .. ")");
				print("Units:");
				for unit in player:Units() do
					print("UNITID: " .. unit:GetID() .. "\t( " .. unit:GetName() .. ", x: " .. unit:GetX() .. ", y: " .. unit:GetY() );
				end
			end
		end
	else
		local player = Players[ arg1 ];
		local unit = player:GetUnitByID( arg2 );

		if ( player:IsHuman() ) then
			local hx, hy  = ToGridFromHex( trackarg3, trackarg4 );
			local plot = Map.GetPlot(hx, hy);
			unit:PopMission();
			unit:PushMission(0, trackarg3, trackarg4, 0, 0, 1, 0, plot, unit);
		else
			moveai = 1;
		end
	end
end

function civstring (player)

	local civinfo;
	if ( not player:IsMinorCiv() ) then civinfo = GameInfo.Civilizations[ player:GetCivilizationType() ];
	else civinfo = GameInfo.MinorCivilizations[ player:GetMinorCivType() ];
	end
	local string = "string";
	if (civinfo ~= nil) then
		string = Locale.ConvertTextKey( civinfo.Description);
	end
	return string;
end
 
I manage to get the function working in case it is useful. What it does is move the settlers to random positions in the map with certain conditions. It could be use for other units.

One thing, the function works, but I cannot call it with Events.SequenceGameInitComplete.Add ( movesettlers ); I will check another post that talk about this. At some point it was calling it and suddenly no???

Code:
function utrack (arg1, arg2, arg3, arg4)

    trackarg1 = arg1;
    trackarg2 = arg2;
    trackarg3 = arg3;
    trackarg4 = arg4;

    local player = Players[ arg1 ];
    local unit = player:GetUnitByID( arg2 );
	local hx, hy  = ToGridFromHex( trackarg3, trackarg4 );
    local plot = Map.GetPlot(hx, hy);            
	unit:SetXY(hx, hy);

end


function validplot (pciv,unittag,arg1,arg2)
  
    valid = 0;
	iterations =0;
	local hx, hy  = ToGridFromHex( arg1, arg2 );
	
	local plot = Map.GetPlot(hx, hy);
	
	--not owned
	if ( not plot:IsOwned() ) then
	  --is passable
	  if ( not plot:IsImpassable() ) then
		--is land
		if ( not plot:IsWater() ) then
			--has no units
			if ( plot:GetNumUnits()==0 ) then
				valid=1;
			end
		end
	  end
	end
	print(valid);

	if (valid==0) then
	    --choose another random spot
		local iW, iH = Map.GetGridSize();
		--validplot (rand)
		posx = math.random (1,iW);
		posy = math.random (1,iH);		
		validplot (pciv,unittag,posx,posy);		
	else
		--order move unit		
		utrack (pciv,unittag,arg1,arg2);		
	end
end


function movesettlers ()
	    local player = Players[ 0 ];            
                for unit in player:Units() do                    					
					if (string.find( unit:GetName(), 'Settler') ) then
						local iW, iH = Map.GetGridSize();						
						--validplot (rand)
						posx = math.random (1,iW);
						posy = math.random (1,iH);
						validplot (0,unit:GetID(),posx,posy);
					end
                end            
end
Events.SequenceGameInitComplete.Add ( movesettlers );


function realstart ()
                print("Really really just started a game!");
end
--Events.SequenceGameInitComplete.Add ( realstart );
 
Top Bottom