Förster

Der Förster an sich ist so angelegt, das er lustig Bäume vor sich hinplanzt. Man kann ihn gut gebrauchen, wenn auf einer Map viele „Wüsten-“ oder Steppengebiete sind. Er schafft jedenfalls eine unerschöpflichen Vorrat an der Resource Holz.

Man muss auf der Map lediglich eine Entity mit Namen: „gaertner1“ setzen und eine ScriptEntity mit Namen: „WaldEntity“. Und Dario darf auch nicht fehlen „dario“.

function DarioIsNeargaertner1()
if IsNear(GetEntityId("dario"), GetEntityId("gaertner1"), 1000) then
    EndJob(DarioIsNeargaertner1ID)
    _OP_Forests_addTreeGrowth()
    MyForest = _OP_Forests_setupForest(GetPosition("WaldEntity"), 500, 1800, "gaertner1")
    end
end

Aufgerufen wird das Ganze mit

DarioIsNeargaertner1ID=StartSimpleJob("DarioIsNeargaertner1");

entweder in der FirstMapAction oder als „Belohnung“ in einem Briefing.Finished.

Falls der Förster pausieren soll, wenn 20 Bäume gepflanzt wurden, kann folgendes verwendet werden:

function PauseForester()
    if _OP_Forests_getNumberOfExistingSeededTrees(MyForest) >= 20 then
        _OP_Forests_disableForester(MyForest)
    else
        _OP_Forests_enableForester(MyForest)
    end
end

Die Bäume wachsen aber trotzdem weiter.

Code

Der folgende Code wird benötigt, damit der Förster funktioniert:

--########################################################################
--###
--### Forest Script
--### Created by Opeter
--### Updated by Old McDonald
--###
 
 
--the forests table
_OP_Forests = {};
--set it local for better handling (the name is too long :))
local f = _OP_Forests;
 
--here you can add some tree entities
--don't forget to add at least one group of trees
f.Grow = {
};
 
--handling triggers...
--some dummy functions
function _OP_Forests_growTrees(_index)
	return _OP_Forests:growTrees(_index);
end
 
function _OP_Forests_plantTrees(_index)
	return _OP_Forests:plantTrees(_index);
end
 
function _OP_Forests_moveForester(_index)
	return _OP_Forests:moveForester(_index);
end
 
--########################################################################
--###
--### Forest Functions
--###
 
--these two functions create forests
function f:createForest(_position, _distance, _range)
	local forest = {};
    local funcAdd = function( _n1, _n2 ) return _n1 + _n2; end;
    local funcSub = function( _n1, _n2 ) return _n1 - _n2; end;
	self:createSegment( forest, _position, _distance, _range, funcAdd, funcAdd );
	self:createSegment( forest, _position, _distance, _range, funcSub, funcAdd );
	self:createSegment( forest, _position, _distance, _range, funcAdd, funcSub );
	self:createSegment( forest, _position, _distance, _range, funcSub, funcSub );
 
	return forest;
end
 
function f:createSegment(_forest, _position, _distance, _range, _opX, _opY)
 
	local distance2 = math.sqrt( _distance * _distance - (_distance/2) * (_distance / 2) );
 
	local forestPos = {
		X = _position.X,
		Y = _position.Y
	};
 
	local x = 0;
	for y = 0, _range / distance2, 1 do
		while self:getRange(_position, forestPos) <= _range do
			local entry = {
				name	= string.format( "_OP_Tree%d%d", table.getn( self ) + 1, table.getn( _forest ) + 1 ),
				status 	= 0,
				type	= 0
			};
 
			entry.position = {
				X = forestPos.X + math.random(100) - 50,
				Y = forestPos.Y + math.random(100) - 50
			};
 
			entry.posF = {
				X = 0,
				Y = 0
			};
 
			if self:testPos(entry.position) then
				entry.ID = CreateEntity( 1, Entities.XD_ScriptEntity, entry.position, entry.name );
				table.insert( _forest, entry );
			end
 
			forestPos.X = _opX( forestPos.X, distance2 );
		end
 
		forestPos.X = _opX( _position.X, _distance / 2 * x );
		forestPos.Y = _opY( forestPos.Y, distance2 );
 
		x = 1 - x;
	end
end
 
--this function let the trees grow
function f:growTrees(_index)
	local forest = self[_index];
 
	if forest == nil then
		--return 
		return true;
	elseif IsDestroyed(forest.forester) then
		--destroy jobs
		if forest.plantTreesJobId ~= 0 then
			Trigger.UnrequestTrigger(forest.plantTreesJobId);
		else
			Trigger.UnrequestTrigger(forest.moveForesterJobId);
		end
		Trigger.UnrequestTrigger(forest.growTreesJobId);
 
		--delete forest table
		self[_index] = nil;
 
		--return
		return true;
	end
 
	forest.counterGrow = forest.counterGrow - 1;
	if forest.counterGrow == 0 then
 
		for i = 1, table.getn(forest), 1 do
			local doFlag, eName = true, forest[i].name;
			local eResource = 0;
			if forest[i].status > 1 and forest[i].status < table.getn(self.Grow[forest[i].type]) + 1 then
				eResource = Logic.GetResourceDoodadGoodAmount(GetEntityId(eName));
				if eResource > 0 then
					doFlag = false;
				end
 
				if Logic.GetEntitiesInArea( 0, forest[i].position.X, forest[i].position.Y, 150, 1 ) > 0 then
					doFlag = false;
				end
 
				local newEntityType = self.Grow[forest[i].type][forest[i].status];
				if newEntityType and not IsDead(eName) and math.random(forest.ProbabilityOfGrowing) == 1 and doFlag then
					forest[i].status = forest[i].status + 1;
					ReplaceEntity(eName, newEntityType);
				end
			end
 
		end
 
		forest.counterGrow = 4;
	end
end
 
 
--this function controls the planting of trees
function f:plantTrees(_index)
	local forest = self[_index];
	if forest == nil then
		--return 
		return true;
	elseif IsDestroyed(forest.forester) then
		--destroy jobs
		if forest.plantTreesJobId ~= 0 then
			Trigger.UnrequestTrigger(forest.plantTreesJobId);
		else
			Trigger.UnrequestTrigger(forest.moveForesterJobId);
		end
		Trigger.UnrequestTrigger(forest.growTreesJobId);
 
		--delete forest table
		self[_index] = nil;
 
		--return
		return true;
	elseif not forest.Enabled then
		return false;
	end
 
	forest.WaitTime = (forest.WaitTime or 0) + 1;
	if forest.WaitTime < forest.Delay then
		--delay not reached
		return false;
	end
 
	forest.counterForester = forest.counterForester - 1;
	if forest.counterForester <= 0 then
 
		for i = 1, table.getn(forest), 1 do
			local eName, ePos = forest[i].name, { X = forest[i].position.X, Y = forest[i].position.Y };
			if forest[i].status == 1 then
				if forest.OKTree then
					if Logic.GetEntitiesInArea(0, ePos.X, ePos.Y, 100, 1) == 0 then
						forest.nextTree = true;
						if ReplaceEntity(eName, Entities.XD_Bush4) > 0 then
							forest[i].status = 2;
							forest[i].type = math.random( table.getn( self.Grow ) );
						else
							forest[i].status = -1;
						end
					else
						if forest[i].posF.X == GetPosition(forest.forester).X and forest[i].posF.Y == GetPosition(forest.forester).Y then
							forest[i].status = 0;
							forest.nextTree = true;
						else
							forest[i].posF.X = GetPosition(forest.forester).X;
							forest[i].posF.Y = GetPosition(forest.forester).Y;
						end
						forest.counterForester = 1;
					end
				else
					forest[i].status = 0;
					forest.nextTree = true;
					forest.counterForester = 1;
				end
			end
 
			if forest[i].status > 1 and IsDead(eName) then
				forest[i].status = 0;
				self:cleanUp(ePos, 200);
				CreateEntity(1, Entities.XD_ScriptEntity, ePos, eName);
			end
		end
		if forest.counterForester == 0 then
			forest.counterForester = 2;
		end
 
		if not forest.nextTree then
			return;
		end
 
		local array = {};
		for i = 1, table.getn(forest), 1 do
			if forest[i].status == 0 then
				table.insert( array, i );
			end
		end
 
		if table.getn( array ) == 0 then
			forest.counterForester = 5;
			return;
		end
 
		local i = array[math.random(table.getn(array))];
 
		local eName = forest[i].name;
		local posF = {
			X = forest[i].position.X - 1,
			Y = forest[i].position.Y - 1
		};
 
		forest.nextTree = false;
		forest[i].status = 1;
		forest.NTree = GetEntityId(eName);
 
		Move(forest.forester, posF);
 
		forest[i].posF = {
			X = GetPosition(forest.forester).X,
			Y = GetPosition(forest.forester).Y
		};
 
		forest.OKTree = true;
		forest.foresterPos = forest[i].posF;
		forest.oldForesterPos = forest[i].posF;
		forest.moveForesterJobId = Trigger.RequestTrigger( Events.LOGIC_EVENT_EVERY_SECOND, nil, "_OP_Forests_moveForester", 1, {}, {_index} );
 
		forest.plantTreesJobId = 0;
		return true;
	end
end
 
--this function controls whether the forester still walks
function f:moveForester(_index)
	local forest = self[_index];
	if forest == nil then
		--return 
		return true;
	elseif IsDestroyed(forest.forester) then
		--destroy jobs
		if forest.plantTreesJobId ~= 0 then
			Trigger.UnrequestTrigger(forest.plantTreesJobId);
		else
			Trigger.UnrequestTrigger(forest.moveForesterJobId);
		end
		Trigger.UnrequestTrigger(forest.growTreesJobId);
 
		--delete forest table
		self[_index] = nil;
 
		--return
		return true;
	elseif not forest.Enabled then
		return false;
	end
 
	if IsNear(forest.forester, forest.NTree, 50) then
		Move(forest.forester, forest.oldForesterPos);
		forest.plantTreesJobId = Trigger.RequestTrigger( Events.LOGIC_EVENT_EVERY_SECOND, nil, "_OP_Forests_plantTrees", 1, {}, {_index} );
		forest.moveForesterJobId = 0;
		forest.counterForester = 3;
		return true;
	elseif forest.foresterPos.X == GetPosition(forest.forester).X and forest.foresterPos.Y == GetPosition(forest.forester).Y then
		forest.OKTree = false;
		forest.plantTreesJobId = Trigger.RequestTrigger( Events.LOGIC_EVENT_EVERY_SECOND, nil, "_OP_Forests_plantTrees", 1, {}, {_index} );
		forest.moveForesterJobId = 0;
		forest.counterForester = 1;
		forest.WaitTime = 0;
		return true;
	else
		forest.oldForesterPos = forest.foresterPos;
		forest.foresterPos = GetPosition(forest.forester);
	end
end
 
--########################################################################
--###
--### Utility Functions
--###
 
function f:getRange(_pos1, _pos2)
	local x, y = _pos2.X - _pos1.X, _pos2.Y - _pos1.Y;
	return math.sqrt( x * x + y * y );
end
 
function f:testPos(_pos)
	if Logic.GetEntitiesInArea( Entities.XD_ScriptEntity, _pos.X, _pos.Y, 200, 1 ) == 0 then
		return true;
	end
end
 
function f:cleanUp(_position, _range)
	local data = { Logic.GetEntitiesInArea( Entities.XD_TreeStump1, _position.X, _position.Y, _range, 16 ) };
	for i = 1, table.remove(data, 1), 1 do
		DestroyEntity(data[i]);
	end
end
 
--########################################################################
--###
--### Comfort Functions
--###
 
--comfort wrapper
function _OP_Forests_setupForest(_position, _distance, _range, _forester, _delay, _probabilityOfGrowing)
	return _OP_Forests:setupForest(_position, _distance, _range, _forester, _delay, _probabilityOfGrowing);
end
 
--this comfort function creates a new forest,  setups the values of the indices in the forest table and starts the jobs
function f:setupForest(_position, _distance, _range, _forester, _delay, _probabilityOfGrowing)
 
	assert(table.getn(self.Grow) > 0, "at least one growth plan must exist");
 
	assert(IsValid(_forester), "the forester must be alive");
 
	local forest = self:createForest(_position, _distance, _range);
	forest.counterForester = 2;
	forest.counterGrow = 4;
	forest.NTree = 0;
	forest.nextTree = true;
	forest.OKTree = true;
	forest.foresterPos = {};
	forest.oldForesterPos = {};
	forest.forester = _forester;
	forest.moveForesterJobId = 0;
	forest.Enabled = true;
	forest.Delay = forest.Delay or 0;
	forest.ProbabilityOfGrowing = _probabilityOfGrowing or 5;
 
	local indexPos = table.getn(self) + 1;
	table.insert(self, forest);
 
	forest.plantTreesJobId = Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_SECOND, nil, "_OP_Forests_plantTrees", 1, {}, {indexPos});
	forest.growTreesJobId = Trigger.RequestTrigger(Events.LOGIC_EVENT_EVERY_SECOND, nil, "_OP_Forests_growTrees", 1, {}, {indexPos});
 
 
	return forest;
end
 
--comfort wrapper
function _OP_Forests_addTreeGrowth( _description )
	return _OP_Forests:addTreeGrowth( _description );
end
 
--this function adds a new tree growth "tree". if _description is nil, the standard table will be used
function f:addTreeGrowth( _description )
	if _description == nil then
		_description = {
			Entities.XD_Pine3,
			Entities.XD_Pine4,
			Entities.XD_Pine6,
			Entities.XD_Pine2
		};
	end
 
	assert(type(_description) == "table", "_description must be a table");
	assert(table.getn(_description) > 0, "one tree type must exist");
 
	table.insert(_description, 1, nil);
	_description.n = table.getn(_description);
 
	table.insert(self.Grow, _description);
end
 
--comfort wrapper
function _OP_Forests_disableForester( _forest )
	return _OP_Forests:disableForester(_forest);
end
 
--this function disables the forester
function f:disableForester( _forest )
	_forest.Enabled = false;
end
 
--comfort wrapper
function _OP_Forests_enableForester( _forest )
	return _OP_Forests:enableForester(_forest);
end
 
--this function enables the forester
function f:enableForester( _forest )
	_forest.Enabled = true;
end
 
function _OP_Forests_getNumberOfExistingSeededTrees( _forest )
	return _OP_Forests:getNumberOfExistingSeededTrees(_forest);
end
 
function _OP_Forests:getNumberOfExistingSeededTrees( _forest )
	local n = 0;
	for i = 1, table.getn(_forest), 1 do
		if _forest[i].status > 1 and not IsDead(_forest[i].name) then
			n = n + 1;
		end
	end
	return n;
end