SupplyFieldGoodsMission = {};
SupplyFieldGoodsMission.REGISTER_XML_PATHS = true;
SupplyFieldGoodsMission.NAME = "supplyFieldGoodsMission";
SupplyFieldGoodsMission.MISSIONS_XML = {};
SupplyFieldGoodsMission.MAX_NUM_INSTANCE = 1;
SupplyFieldGoodsMission.NUM_INSTANCE = 0;
SupplyFieldGoodsMission.MAX_NUM_DAY = {5,0,1}; --max,is,nextDay + ingame day
SupplyFieldGoodsMission.LAST_FIELD_TRIGGER = 0;

SupplyFieldGoodsMission.data = {	
	ownTable = {VEHICLE_USE_COST=50, classOverlay="tractor", typOverlay="trailer"};	
	reward = {dismiss=0, min=3000, PER_METER=2, PER_OBJECT=700, NUM_OBJECTS_PER_DRIVE=2};
	jobTypName = g_i18n:getText("ai_jobTitleLoadAndDeliver"); --g_i18n:getText("contract_typ_supply_field_goods");
};

SupplyFieldGoodsMission.metadata = {
	interface = "FS25 ...", --new
	title = "Field supply goods Contracts",
	notes = "Dieser Mod generiert Feld Auslieferungs Aufträge.",
	author = "(by HappyLooser)",		
	build = 3,
	datum = "20.09.2025",
	update = "27.10.2025",
	debugPrint = false, 
	discord = "HappyLooser Modding",
	info = " Link Freigabe,Änderungen,Kopien oder Code Benutzung ist ohne meine Zustimmung nicht erlaubt",
	"##Orginal Link Freigabe: https://www.farming-simulator.com/mods.php"
};

local SupplyFieldGoodsMission_mt = Class(SupplyFieldGoodsMission, AbstractMission);
InitObjectClass(SupplyFieldGoodsMission, "SupplyFieldGoodsMission");

function SupplyFieldGoodsMission.new(isServer, isClient)	
	local title = g_i18n:getText("contract_universal_title");
	local description = SupplyFieldGoodsMission.data.jobTypName;	
	local self = AbstractMission.new(isServer, isClient, title, description, SupplyFieldGoodsMission_mt);
	
	--local uiScale = g_currentMission.hlUtils.getUiScale();
	--self.progressTitle = title.. " | ".. self.data.jobTypName;
	self.progressTitle = "*".. self.data.jobTypName;
	self.jobTypName = "*".. self.data.jobTypName;	
	
	self.loadingObjects = AdditionalContractsLoading.new();	
	self.triggers = {};
	self.objects = {};
	self.objectsAtTrigger = {};
	self.numFinished = 0;
	self.mapHotspots = {};
	self.farmlandId = FarmlandManager.NO_OWNER_FARM_ID;	--GUI does not do a nil check so just use 0
	--self.field = {fieldId=0,getName=function()return ""end}; -- or ... GUI does not do a nil check so just use 0
	SupplyFieldGoodsMission.NUM_INSTANCE = SupplyFieldGoodsMission.NUM_INSTANCE + 1;	
	return self;
end;

function SupplyFieldGoodsMission.registerSavegameXMLPaths(schema, key)
	SupplyFieldGoodsMission:superClass().registerSavegameXMLPaths(schema, key);	
	local supplyFieldGoodsKey = string.format("%s.supplyFieldGoods", key);
	schema:register(XMLValueType.STRING, supplyFieldGoodsKey .. "#missionConfig", "missionConfig name");
	schema:register(XMLValueType.INT, supplyFieldGoodsKey .. "#objectConfig", "objcetConfig index");
	schema:register(XMLValueType.FLOAT, supplyFieldGoodsKey .. "#pickupTriggerX", "pickup trigger position x");
	schema:register(XMLValueType.FLOAT, supplyFieldGoodsKey .. "#pickupTriggerZ", "pickup trigger position z");	
	schema:register(XMLValueType.INT, supplyFieldGoodsKey .. "#dropoffTriggerId", "dropoff trigger Id");
	schema:register(XMLValueType.STRING, supplyFieldGoodsKey .. "#dropoffTriggerTyp", "dropoff trigger typ");	
	schema:register(XMLValueType.INT, supplyFieldGoodsKey .. "#numObjects", "Num objects spawned");
	schema:register(XMLValueType.STRING, supplyFieldGoodsKey .. ".object(?)#uniqueId", "UniqueId of created object");
end;

function SupplyFieldGoodsMission:getOwnData()
	return self.data;
end;

function SupplyFieldGoodsMission:getNumObjects()
	return Utils.getNoNil(self.numObjects, #self.objects);
end;

function SupplyFieldGoodsMission:getMissionTypeName()
    return UniversalMission.NAME;
end;

function SupplyFieldGoodsMission:finish(success)
	SupplyFieldGoodsMission:superClass().finish(self, success);	
	if g_currentMission:getIsServer() then				
		if success then
			g_farmManager:getFarmById(self.farmId).stats:updateMissionDone();
		end;		
	end;	
	if g_currentMission:getFarmId() == self.farmId then
		if success == MissionFinishState.SUCCESS then
			g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_OK, string.format(g_i18n:getText("contract_universal_finish"), self.jobTypName));
		elseif success == MissionFinishState.FAILED or success == MissionFinishState.CANCELED or success == MissionFinishState.TIMED_OUT then
			g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_CRITICAL, string.format(g_i18n:getText("contract_universal_failed"), self.jobTypName));
		end;
	end;
end;

function SupplyFieldGoodsMission:dismiss(mission)
	SupplyFieldGoodsMission:superClass().dismiss(self);
end;

function SupplyFieldGoodsMission:delete()	
	for _, object in pairs(self.objects) do
		self:deleteObject(object);
	end;
	self.objects = {};
	if SupplyFieldGoodsMission.NUM_INSTANCE > 0 then SupplyFieldGoodsMission.NUM_INSTANCE = SupplyFieldGoodsMission.NUM_INSTANCE - 1;end;
	self:destroyHotspots();
	self:removeMissionTriggers();
	SupplyFieldGoodsMission:superClass().delete(self);
end;

function SupplyFieldGoodsMission:deleteObject(object)
	if object ~= nil and object.rootNode ~= nil and entityExists(object.rootNode) then
		g_currentMission.nodeToObject[object.rootNode]:delete();			
	end;
end;

function SupplyFieldGoodsMission:saveToXMLFile(xmlFile, key)	
	xmlFile:setValue(key .. "#typName", SupplyFieldGoodsMission.NAME);
	local supplyFieldGoodsKey = string.format("%s.supplyFieldGoods", key);
	xmlFile:setValue(supplyFieldGoodsKey .. "#missionConfig", self.missionConfig.name);
	xmlFile:setValue(supplyFieldGoodsKey .. "#objectConfig", self.objectConfig.index);
	xmlFile:setValue(supplyFieldGoodsKey .. "#pickupTriggerX", self.pickup.x);
	xmlFile:setValue(supplyFieldGoodsKey .. "#pickupTriggerZ", self.pickup.z);	
	xmlFile:setValue(supplyFieldGoodsKey .. "#dropoffTriggerId", self.dropoff.triggerId);
	xmlFile:setValue(supplyFieldGoodsKey .. "#dropoffTriggerTyp", self.dropoff.triggerTyp);	
	xmlFile:setValue(supplyFieldGoodsKey .. "#numObjects", self.numObjects);
		
	local int = 0
	for _, object in pairs(self.objects) do
		local uniqueId = object:getUniqueId();		
		local objectKey = string.format("%s.object(%d)", supplyFieldGoodsKey, int);
		xmlFile:setValue(objectKey .. "#uniqueId", uniqueId);		
		int = int + 1;
	end;	
	SupplyFieldGoodsMission:superClass().saveToXMLFile(self, xmlFile, key);	
end;

function SupplyFieldGoodsMission:loadFromXMLFile(xmlFile, key)
	local supplyFieldGoodsKey = string.format("%s.supplyFieldGoods", key);
	if not xmlFile:hasProperty(supplyFieldGoodsKey .. "#missionConfig") then return false;end;
	local missionConfig = xmlFile:getValue(supplyFieldGoodsKey .. "#missionConfig");	
	self.missionConfig = self:getTransportMissionConfigByName(missionConfig);
	if self.missionConfig == nil then
		return false;
	end;
	local objectConfig = xmlFile:getValue(supplyFieldGoodsKey .. "#objectConfig");
	self.objectConfig = self:getTransportMissionObjectConfigByIndex(objectConfig);
	if self.objectConfig == nil then
		return false;
	end;
	self.numObjects = xmlFile:getValue(supplyFieldGoodsKey .. "#numObjects");
	local pickupTriggerX = xmlFile:getValue(supplyFieldGoodsKey .. "#pickupTriggerX");
	local pickupTriggerZ = xmlFile:getValue(supplyFieldGoodsKey .. "#pickupTriggerZ");
	local dropoffTriggerId = xmlFile:getValue(supplyFieldGoodsKey .. "#dropoffTriggerId");
	local dropoffTriggerTyp = xmlFile:getValue(supplyFieldGoodsKey .. "#dropoffTriggerTyp");
	
	self.pendingLoadingData = {
		["dropoff"] = {triggerId=dropoffTriggerId, triggerTyp=dropoffTriggerTyp};
		["pickup"] = {x=pickupTriggerX, z=pickupTriggerZ};
		["objects"] = {};
	};
	
	--if self.status == MissionStatus.RUNNING then
		local int = 0;
		while true do
			local objectKey = string.format("%s.object(%d)", supplyFieldGoodsKey, int);
			if not xmlFile:hasProperty(objectKey .. "#uniqueId") then
				break;
			end;
			local uniqueId = xmlFile:getValue(objectKey .. "#uniqueId");
			table.insert(self.pendingLoadingData.objects, uniqueId);			
			int = int + 1;			
		end;
	--end
	self:setLeasingVehicles();
	SupplyFieldGoodsMission.LAST_FIELD_TRIGGER = dropoffTriggerId;
	if not SupplyFieldGoodsMission:superClass().loadFromXMLFile(self, xmlFile, key) then
		return false;
	end;
	return true;
end;

function SupplyFieldGoodsMission:loadMapData(xmlFile, key, baseDirectory)
	
end;

function SupplyFieldGoodsMission:unloadMapData()
	
end;

function SupplyFieldGoodsMission:onSavegameLoaded()
	if self.pendingLoadingData ~= nil then
		local dropoff = nil;
		if self.pendingLoadingData.dropoff.triggerTyp == "field" then dropoff = g_additionalContractMapData:getFieldById(self.pendingLoadingData.dropoff.triggerId);end;
		if dropoff ~= nil then 
			self.dropoff = dropoff;
			if g_server ~= nil then
				local trigger = UniversalMissionFakeTrigger.new(dropoff.triggerId, 1, g_dedicatedServer == nil, true);			
				self:addMissionTrigger(trigger);
				if trigger.mission == nil then trigger:setMission(self);end;
			end;
		end;
		local pickup = g_additionalContractMapData:getStorePlaceTriggerByPosition(self.pendingLoadingData.pickup.x, self.pendingLoadingData.pickup.z);
		if pickup ~= nil then
			self.pickup = pickup; --on server
		else
			self.pickup = {x=self.pendingLoadingData.pickup.x, z=self.pendingLoadingData.pickup.z}; --on client
		end;
		for _, uniqueId in ipairs(self.pendingLoadingData.objects) do
			local vehicle = g_currentMission.vehicleSystem:getVehicleByUniqueId(uniqueId);
			if vehicle ~= nil then				
				self.objects[vehicle.rootNode] = vehicle;
			end;
		end;
		self:setTypDescription();
	end;	
	
	SupplyFieldGoodsMission:superClass().onSavegameLoaded(self)
end;

function SupplyFieldGoodsMission:onStartMap(args)	
	
end;

function SupplyFieldGoodsMission.canRun()	
	if (g_server ~= nil and g_additionalContractMapData:getStorePlaceTrigger() == nil) or #SupplyFieldGoodsMission.MISSIONS_XML == 0 or not g_additionalContractMapData:hasMinFields(10) then 
		g_additionalContractTypes:removeBalanceTyp(SupplyFieldGoodsMission.NAME);
		g_additionalContractTypes:removeRandomTyp(SupplyFieldGoodsMission.NAME);
		g_additionalContractTypes:removeNextDayTyp(SupplyFieldGoodsMission.NAME);
		return false;
	end;
	return not g_additionalContractTypes:isMaxNextDayTyp(SupplyFieldGoodsMission.NAME) and SupplyFieldGoodsMission.NUM_INSTANCE < SupplyFieldGoodsMission.MAX_NUM_INSTANCE;
end;

function SupplyFieldGoodsMission:getTriggerInfo(index, isPickup) --md mod
	local triggers = self.missionConfig.dropoffTriggers;
	if isPickup then
		triggers = self.missionConfig.pickupTriggers;
	end;
	for _, info in ipairs(triggers) do
		if info.index == index then
			return info;
		end;
	end;
	return {};
end;

function SupplyFieldGoodsMission:getTriggerTitle(index, isPickup) --md mod
	return "";
	--local info = self:getTriggerInfo(index, isPickup);
	--return info == nil and "" or g_i18n:convertText(Utils.getNoNil(info.title, ""));
end;

--function SupplyFieldGoodsMission:getActiveTriggers() --md mod
--	return self.pickup, self.dropoff, self:getTriggerTitle(self.dropoff, false);
--end;

function SupplyFieldGoodsMission:getTriggerHotspot(isPickup) --md mod
	if isPickup then
		return nil; --self.pickup;
	end;
	return self.dropoff;
end;

function SupplyFieldGoodsMission:getNPC()	
	local npc = nil; --g_npcManager:getNPCByIndex(self.missionConfig.npcIndex);
	if npc == nil then npc = g_npcManager:getNPCByIndex(1);end;
	return npc;
end;

function SupplyFieldGoodsMission:getJobTypName()
	return self.data.jobTypName;	
end;

function SupplyFieldGoodsMission:getRewards()
	return SupplyFieldGoodsMission.data.reward;
end;

function SupplyFieldGoodsMission:setOwnTable(ownTable)
	self.data.ownTable = ownTable;
end;

function SupplyFieldGoodsMission:getOnwTable()
	return self.data.ownTable;
end;

function SupplyFieldGoodsMission:setOwnTableValue(key, value)
	if key == nil then return;end;
	if self.data.ownTable[key] == nil then self.data.ownTable[key] = {};end;
	self.data.ownTable[key] = value;
end;

function SupplyFieldGoodsMission:getOwnTableValue(key)
	if key == nil or self.data.ownTable[key] == nil then return;end;
	return self.data.ownTable[key];
end;

function SupplyFieldGoodsMission:start(spawnVehicles)	
	if not self:loadObjects() then
		return false;
	end;
	if not SupplyFieldGoodsMission:superClass().start(self, spawnVehicles) then
		return false;
	end;
	return true;	
end;

function SupplyFieldGoodsMission:init()	
	local transportMission = table.getRandomElement(SupplyFieldGoodsMission.MISSIONS_XML);
	if transportMission == nil then return false;end;
	local object = table.getRandomElement(transportMission.objects);
	if object == nil then return false;end;
	local dropoffTrigger = g_additionalContractMapData:getRandomFieldForMission(SupplyFieldGoodsMission.LAST_FIELD_TRIGGER, 3);
	if dropoffTrigger == nil then return false;end;	
	local pickupTrigger = g_additionalContractMapData:getStorePlaceTrigger(2, true);
	if pickupTrigger == nil then return false;end;	
	self.numObjects = g_additionalContractUtils:getIntegerToVector(object.minMax);	
	
	if not g_currentMission.slotSystem:getCanAddLimitedObjects(SlotSystem.LIMITED_OBJECT_PALLET, self.numObjects) then return false;end;
	SupplyFieldGoodsMission.LAST_FIELD_TRIGGER = dropoffTrigger.triggerId;
	
	self.pickup = pickupTrigger;
	self.dropoff = dropoffTrigger;	
	local trigger = UniversalMissionFakeTrigger.new(self.dropoff.triggerId, 1, g_dedicatedServer == nil, true);
	self:addMissionTrigger(trigger);
	if trigger.mission == nil then trigger:setMission(self);end;		
	self.missionConfig = transportMission;
	self.objectConfig = object;		
	self:setTypDescription();
	self.reward = self:calculateReward(object.rewardScale);
	self:setMinReward();
	self:setLeasingVehicles();	
	return SupplyFieldGoodsMission:superClass().init(self);		
end;

function SupplyFieldGoodsMission:setTypDescription()
	local typDescription = self:getTypDesciption();
	self.description = typDescription;	
end;

function SupplyFieldGoodsMission:addMissionTrigger(trigger)	
	self.triggers[trigger.index] = trigger;	
end;

function SupplyFieldGoodsMission:removeMissionTrigger(trigger)	
	if trigger ~= nil and trigger.index ~= nil then 
		self.triggers[trigger.index]:delete();
		self.triggers[trigger.index] = nil;		
	end;	
end;

function SupplyFieldGoodsMission:removeMissionTriggers()
	for _, trigger in pairs(self.triggers) do
		trigger:delete();		
	end;
	self.triggers = {};	
end;

function SupplyFieldGoodsMission:writeStream(streamId, connection) --server
	streamWriteUInt8(streamId, self.numObjects);
	streamWriteUInt8(streamId, self.missionConfig.id);
	streamWriteUInt8(streamId, self.objectConfig.index);	
	----	
	streamWriteString(streamId, tostring(self.dropoff.triggerTyp));	
	streamWriteInt32(streamId, self.dropoff.triggerId);
	streamWriteFloat32(streamId, self.pickup.x);
	streamWriteFloat32(streamId, self.pickup.z);	
	SupplyFieldGoodsMission:superClass().writeStream(self, streamId, connection);
end;

function SupplyFieldGoodsMission:readStream(streamId, connection) --client
	self.numObjects = streamReadUInt8(streamId);	
	self.missionConfig = self:getTransportMissionConfigById(streamReadUInt8(streamId));
	self.objectConfig = self:getTransportMissionObjectConfigByIndex(streamReadUInt8(streamId));
	self:setTypDescription();
	self:setLeasingVehicles();
	----
	local dropoffTriggerTyp = streamReadString(streamId);
	local dropoffTriggerId = streamReadInt32(streamId);
		
	local pickupTriggerX = streamReadFloat32(streamId);		
	local pickupTriggerZ = streamReadFloat32(streamId);
		
	self.pendingLoadingData = {
		["dropoff"] = {triggerId=dropoffTriggerId, triggerTyp=dropoffTriggerTyp};
		["pickup"] = {x=pickupTriggerX, z=pickupTriggerZ};
	};	
	SupplyFieldGoodsMission:superClass().readStream(self, streamId, connection);
end;

function SupplyFieldGoodsMission:triggerUpdate(trigger, dt)	
	if self.status == MissionStatus.RUNNING and trigger.triggerId ~= nil then		
		local field = g_fieldManager:getFieldById(trigger.triggerId);
		if field ~= nil then			
			local xD, zD = field:getCenterOfFieldWorldPosition();
			local yD = 0;			
			for _, object in pairs(self.objects) do				
				local nodeId = object.rootNode;				
				if nodeId ~= nil and (self.objectsAtTrigger[nodeId] == nil or not self.objectsAtTrigger[nodeId]) then					
					local xV, yV, zV = getWorldTranslation(nodeId);
					
					local farmlandId = g_farmlandManager:getFarmlandIdAtWorldPosition(xV, zV);
					if farmlandId ~= nil and farmlandId > 0 and farmlandId == self.dropoff.farmlandId then
						--local terrainHeightY = getTerrainHeightAtWorldPos(g_terrainNode, xV, 0, zV);					
						--if yV <= terrainHeightY + 0.5 then
							self.numFinished = self.numFinished + 1;
							self.objectsAtTrigger[nodeId] = true;
						--end;
					end;			
				end;				
			end;		
		end;
	end;	
end;

function SupplyFieldGoodsMission:getTransportMissionConfigById(id)
	for _, transportMission in pairs(SupplyFieldGoodsMission.MISSIONS_XML) do
		if transportMission.id == id then
			return transportMission;
		end;
	end;
	return nil;
end;

function SupplyFieldGoodsMission:getTransportMissionConfigByName(name)
	for _, transportMission in pairs(SupplyFieldGoodsMission.MISSIONS_XML) do
		if transportMission.name == name then
			return transportMission;
		end;
	end;
	return nil;
end;

function SupplyFieldGoodsMission:getTransportMissionObjectConfigByIndex(index)
	for _, object in pairs(self.missionConfig.objects) do
		if object.index == index then
			return object;
		end;
	end;	
end;

function SupplyFieldGoodsMission:setMinReward()
	if self.reward < SupplyFieldGoodsMission.data.reward.min then self.reward = SupplyFieldGoodsMission.data.reward.min;end;	
end;

function SupplyFieldGoodsMission:setLeasingVehicles()	
	if self.missionConfig.typ ~= nil then
		-- if g_additionalContractTypes:hasAutoload() then			
			-- if self.missionConfig.typ == "CONSUMABLES_TANK" and self.numObjects <= 2 then
				-- self.objectConfig.leasingSize = "small_supplyFieldGoodsMission";
				-- self.objectConfig.leasingVariant = "SMALL_AUTOLOAD";
			-- elseif self.missionConfig.typ == "CONSUMABLES_BIGBAGS" or self.numObjects < 3 then
				-- self.objectConfig.leasingSize = "small_supplyFieldGoodsMission";
				-- self.objectConfig.leasingVariant = "SMALL_AUTOLOAD";
			-- else	
				-- self.objectConfig.leasingSize = "medium_supplyFieldGoodsMission";
				-- self.objectConfig.leasingVariant = "MEDIUM_AUTOLOAD";
			-- end;			
		-- else
			if self.missionConfig.typ == "CONSUMABLES_TANK" and self.numObjects <= 2 then
				self.objectConfig.leasingSize = "small_supplyFieldGoodsMission";
				self.objectConfig.leasingVariant = "SMALL_DEFAULT";
			elseif self.missionConfig.typ == "CONSUMABLES_BIGBAGS" or self.numObjects < 3 then
				self.objectConfig.leasingSize = "small_supplyFieldGoodsMission";
				self.objectConfig.leasingVariant = "SMALL_DEFAULT";
			else
				self.objectConfig.leasingSize = "medium_supplyFieldGoodsMission";
				self.objectConfig.leasingVariant = "MEDIUM_DEFAULT";
			end;
		--end;
	end;
end;

function SupplyFieldGoodsMission:calculateReward(rewardScale)	
	local pickupTrigger = self:getPickupTrigger();
	local dropoffTrigger = self:getDropoffTrigger();
	local distance = g_additionalContractUtils:getObjectDistance( {x=dropoffTrigger.x, z=dropoffTrigger.z}, {x=pickupTrigger.x, z=pickupTrigger.z} );
	local driveReward = self.numObjects / SupplyFieldGoodsMission.data.reward.NUM_OBJECTS_PER_DRIVE;	
	if g_additionalContractTypes:hasAutoload() then rewardScale = rewardScale/1.5;end;
	return (math.ceil(driveReward) * SupplyFieldGoodsMission.data.reward.PER_METER * distance + self.numObjects * SupplyFieldGoodsMission.data.reward.PER_OBJECT) * rewardScale;	
end;

function SupplyFieldGoodsMission:getReward()
	local reward = self.reward; -- * g_additionalContractTypes.multiplier;
	if reward < self.reward then reward = self.reward;end;
	return reward;
end;

function SupplyFieldGoodsMission:calculateStealingCost()	
	return 0;
end;

function SupplyFieldGoodsMission:getStealingCosts()	
	return self:calculateStealingCost();
end;

function SupplyFieldGoodsMission:getVehicleSize()	
	if self.objectConfig == nil or self.objectConfig.leasingSize == nil then return nil;end;
	return self.objectConfig.leasingSize;	
end;

function SupplyFieldGoodsMission:getVehicleVariant()	
	if self.objectConfig == nil or self.objectConfig.leasingVariant == "NONE" then return nil;end;
	local leasingSize = self:getVehicleSize();	
	if leasingSize == nil or leasingSize == "NONE" then return nil;end;	
	return self.objectConfig.leasingVariant;	
end;

function SupplyFieldGoodsMission:getVehicleCosts()
	if self.vehiclesToLoad == nil then
		return 0;
	end;
	local numVehicles = #self.vehiclesToLoad;
	local difficultyMultiplier = 0.7 + 0.3 * g_currentMission.missionInfo.economicDifficulty;
	return numVehicles * SupplyFieldGoodsMission.data.ownTable.VEHICLE_USE_COST * difficultyMultiplier;
end;

function SupplyFieldGoodsMission:getFarmlandId()
   return self.farmlandId or FarmlandManager.NO_OWNER_FARM_ID; --GUI does not do a nil check so just use 0 / Error: Running LUA method 'update'. dataS/scripts/gui/InGameMenuContractsFrame.lua:572: attempt to compare nil < nil
end;

function SupplyFieldGoodsMission:getDetails()	
	local details = SupplyFieldGoodsMission:superClass().getDetails(self);	
	local dropoffTrigger = self:getDropoffTrigger();
	if dropoffTrigger ~= nil then
		local fieldTyp = {
			["title"] = g_i18n:getText("contract_details_farmland").. "(".. g_i18n:getText("contract_details_field").. ")";
			["value"] = tostring(dropoffTrigger.triggerId);
		};	
		table.insert(details, fieldTyp);
	end;
	local category = {
		--["title"] = g_i18n:getText("finance_purchaseConsumable");
		["title"] = tostring(self.numObjects).. "x ".. g_i18n:getText(self.objectConfig.category);
		["value"] = g_i18n:getText(self.objectConfig.typTitle);
	};
	table.insert(details, category);
	return details;
end;

function SupplyFieldGoodsMission:update(dt)
	SupplyFieldGoodsMission:superClass().update(self, dt);	
	if self.status == MissionStatus.RUNNING and g_localPlayer ~= nil and g_localPlayer.farmId == self.farmId and not self:hasHotspots() then		
		self:createHotspots();		
	end;	
end;

function SupplyFieldGoodsMission:getMapHotspots()
	return self.mapHotspots;
end;

function SupplyFieldGoodsMission:hasHotspots()
	return self.pickupHotspot ~= nil and self.dropoffHotspot ~= nil and g_additionalContractMapData:isDataLoaded();	
end;

function SupplyFieldGoodsMission:createHotspots()
	if self:getDropoffTrigger() == nil and self.pendingLoadingData ~= nil and self.pendingLoadingData.dropoff ~= nil then self:setDropoffTrigger();end;
	if self:getDropoffTrigger() == nil then return;end; --mp waiting login
	if self.dropoffHotspot == nil then self.dropoffHotspot = self:createHotspot(self:getDropoffTrigger(), "dropoff", self.objectConfig.dropoffHotspotTyp, false, "mangenta");end; --mangenta ls25active
	
	if self:getPickupTrigger() == nil and self.pendingLoadingData ~= nil and self.pendingLoadingData.pickup ~= nil then self:setPickupTrigger();end;
	if self:getPickupTrigger() == nil then return;end; --mp waiting login
	if self.pickupHotspot == nil then self.pickupHotspot = self:createHotspot(self:getPickupTrigger(), "pickup", self.objectConfig.pickupHotspotTyp, false, "mangenta", self.objectConfig.fillType);end;	
end;

function SupplyFieldGoodsMission:destroyHotspots()
	if self.pickupHotspot ~= nil then
		g_currentMission:removeMapHotspot(self.pickupHotspot);
		self.pickupHotspot:delete();
		self.pickupHotspot = nil;
	end;
	if self.dropoffHotspot ~= nil then
		g_currentMission:removeMapHotspot(self.dropoffHotspot);
		self.dropoffHotspot:delete();
		self.dropoffHotspot = nil;
	end;
end;

function SupplyFieldGoodsMission:visibleHotspot(hotspot, state)
	if hotspot == "pickupHotspot" then
		if self.pickupHotspot ~= nil then			
			self.pickupHotspot:setVisible(state);			
		end;
	elseif hotspot == "dropoffHotspot" then
		if self.dropoffHotspot ~= nil then			
			self.dropoffHotspot:setVisible(state);			
		end;
	end;
end;

function SupplyFieldGoodsMission:setPickupTrigger()
	self.pickup = g_additionalContractMapData:getStorePlaceTriggerByPosition(self.pendingLoadingData.pickup.x, self.pendingLoadingData.pickup.z);
	self.pendingLoadingData.pickup = nil;
end;

function SupplyFieldGoodsMission:getPickupTrigger()	
	return self.pickup;	
end;

function SupplyFieldGoodsMission:setDropoffTrigger()	
	if self.pendingLoadingData.dropoff.triggerTyp == "field" then self.dropoff = g_additionalContractMapData:getFieldById(self.pendingLoadingData.dropoff.triggerId);end;
	if self.dropoff ~= nil then self.pendingLoadingData.dropoff = nil;end;
end;

function SupplyFieldGoodsMission:getDropoffTrigger()	
	return self.dropoff;	
end;

function SupplyFieldGoodsMission:createHotspot(trigger, typ, hotspotTyp, blinking, color, fillTypeName)
	if trigger == nil or not g_additionalContractUtils:isLoadedHotspotOverlays() then return nil;end; --MP
	local x, _, z;
	if trigger.x ~= nil and trigger.z ~= nil then x = trigger.x;z = trigger.z;end;
	if x == nil then return nil;end;
	local mapHotspot = UniversalMissionHotspot.new(nil, self.farmId, typ, hotspotTyp, blinking, color, fillTypeName);
	mapHotspot:setWorldPosition(x, z);
	g_currentMission:addMapHotspot(mapHotspot);		
	local contractsFrameMapHotspot = UniversalCircleMissionHotspot.new(); --fake for InGameMenuContractsFrame
	contractsFrameMapHotspot:setWorldPosition(x, z);
	table.addElement(self.mapHotspots, contractsFrameMapHotspot);
	return mapHotspot;
end;

function SupplyFieldGoodsMission:getCompletion()
	return self.numFinished / self.numObjects;
end;

function SupplyFieldGoodsMission:loadObjects()
	local trigger = self:getPickupTrigger();
	local objectConfig = self.objectConfig;	
	if objectConfig == nil then
		return false;
	end;
	local canSpawn, objectsSpawnPosition = g_additionalContractUtils:getObjectsSpawnPosition(trigger, nil, self.numObjects, objectConfig);
	if not canSpawn or #objectsSpawnPosition <= 0 then return false;end;
	for o=1, #objectsSpawnPosition do		
		self.loadingObjects:spawnObject(self, objectsSpawnPosition[o], trigger, objectConfig);		
	end;	
	return true;
end;

function SupplyFieldGoodsMission:loadInit()
	g_additionalContractTypes:insertRandomTyp(SupplyFieldGoodsMission.NAME);
	g_additionalContractTypes:insertBalanceTyp(SupplyFieldGoodsMission.NAME);
	g_additionalContractTypes:insertNextDayTyp(SupplyFieldGoodsMission.NAME, SupplyFieldGoodsMission.MAX_NUM_DAY);	
	local xmlFilename = Utils.getFilename("missions/universalMission/supplyFieldGoodsMission/missions.xml", AdditionalContracts.modDir);
	if xmlFilename ~= nil and xmlFilename ~= "" then SupplyFieldGoodsMission.MISSIONS_XML = g_additionalContractUtils:loadMissionsXml(xmlFilename);end;
	xmlFilename = Utils.getFilename("missionVehicles/supplyFieldGoodsMissionVehicles.xml", AdditionalContracts.modDir)
	if xmlFilename ~= nil and xmlFilename ~= "" then g_missionManager:addPendingMissionVehiclesFile(xmlFilename, AdditionalContracts.modDir);end;	
end;

function SupplyFieldGoodsMission:getTypDesciption()
	if g_additionalContractUtils:isLanguageAvailable() then
		return g_i18n:getText("typDescription_supply_field_goods");
	else
		return g_i18n:getText("ai_jobTitleLoadAndDeliver").. ": ".. g_i18n:getText("ui_dealer").. " --> ".. g_i18n:getText("contract_details_farmland").. "(".. g_i18n:getText("contract_details_field").. ")";
	end;
end;

function SupplyFieldGoodsMission:getJobTypName()
	return self.data.jobTypName;
end;

function SupplyFieldGoodsMission:getClassTypOverlay(args)
	local classOverlay = "tractor";
	local typOverlay = "trailer";
	if self.objectConfig.classOverlay ~= nil then classOverlay = self.objectConfig.classOverlay;end;
	if self.objectConfig.typOverlay ~= nil then typOverlay = self.objectConfig.typOverlay;end;	
	return classOverlay, typOverlay;
end;

function SupplyFieldGoodsMission:getFillTypeIndexOverlay(args)
	local fillType = g_fillTypeManager:getFillTypeByName(self.objectConfig.fillType);
	if fillType ~= nil then return fillType.index;end;
end;

---balance system---
function SupplyFieldGoodsMission:setBalance()
	
end;
---balance system---

g_additionalContractTypes:registerTyp(SupplyFieldGoodsMission, SupplyFieldGoodsMission.NAME);
