VehicleTransportMission = {};
VehicleTransportMission.REGISTER_XML_PATHS = true;
VehicleTransportMission.NAME = "vehicleTransportMission";
VehicleTransportMission.MISSIONS_XML = {};
VehicleTransportMission.MAX_NUM_INSTANCE = 1;
VehicleTransportMission.NUM_INSTANCE = 0;
VehicleTransportMission.TARGET_TRIGGER_DISTANCE = 500; --dropoff trigger
VehicleTransportMission.MAX_NUM_DAY = {5,0,1}; --max,is,nextDay + ingame day

VehicleTransportMission.data = {	
	ownTable = {VEHICLE_USE_COST=120, classOverlay="tractor", typOverlay="unknownTrailer"};	
	reward = {dismiss=0, min=2000, PER_METER=5, PER_OBJECT=2000, NUM_OBJECTS_PER_DRIVE=2};
	jobTypName = g_i18n:getText("contract_typ_transport_vehicle");	
};

VehicleTransportMission.metadata = {
	interface = "FS25 ...", --new
	title = "Vehicle Transport Contracts",
	notes = "Dieser Mod generiert Fahrzeug Transport Aufträge.",
	author = "(by HappyLooser)",		
	build = 6,
	datum = "10.08.2025",
	update = "06.02.2026",
	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 VehicleTransportMission_mt = Class(VehicleTransportMission, AbstractMission);
InitObjectClass(VehicleTransportMission, "VehicleTransportMission");

function VehicleTransportMission.new(isServer, isClient)	
	local title = g_i18n:getText("contract_universal_title");
	local description = g_i18n:getText("typDescription_vehicle_towing_workshop");	
	local self = AbstractMission.new(isServer, isClient, title, description, VehicleTransportMission_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
	VehicleTransportMission.NUM_INSTANCE = VehicleTransportMission.NUM_INSTANCE + 1;	
	return self;
end;

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

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

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

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

function VehicleTransportMission:finish(success)
	VehicleTransportMission: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 VehicleTransportMission:dismiss(mission)
	VehicleTransportMission:superClass().dismiss(self);
end;

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

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

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

function VehicleTransportMission:loadFromXMLFile(xmlFile, key)
	local vehicleTransportKey = string.format("%s.vehicleTransport", key);
	if not xmlFile:hasProperty(vehicleTransportKey .. "#missionConfig") then return false;end;
	local missionConfig = xmlFile:getValue(vehicleTransportKey .. "#missionConfig");	
	self.missionConfig = self:getTransportMissionConfigByName(missionConfig);
	if self.missionConfig == nil then
		return false;
	end;
	local objectConfig = xmlFile:getValue(vehicleTransportKey .. "#objectConfig");
	self.objectConfig = self:getTransportMissionObjectConfigByIndex(objectConfig);
	if self.objectConfig == nil then
		return false;
	end;
	self.numObjects = xmlFile:getValue(vehicleTransportKey .. "#numObjects");
	local pickupTriggerX = xmlFile:getValue(vehicleTransportKey .. "#pickupTriggerX");
	local pickupTriggerZ = xmlFile:getValue(vehicleTransportKey .. "#pickupTriggerZ");
	local dropoffTriggerUniqueId = xmlFile:getValue(vehicleTransportKey .. "#dropoffTriggerUniqueId");
	local dropoffTriggerTyp = xmlFile:getValue(vehicleTransportKey .. "#dropoffTriggerTyp");
	
	self.pendingLoadingData = {
		["dropoff"] = {uniqueId=dropoffTriggerUniqueId, 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)", vehicleTransportKey, 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	
	if not VehicleTransportMission:superClass().loadFromXMLFile(self, xmlFile, key) then
		return false;
	end;
	return true;
end;

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

function VehicleTransportMission:unloadMapData()
	
end;

function VehicleTransportMission:onSavegameLoaded()
	if self.pendingLoadingData ~= nil then
		local dropoff = g_additionalContractMapData:getTriggerByUniqueId(self.pendingLoadingData.dropoff.uniqueId);
		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_additionalContractSplineManager:getSplineByPosition(self.pendingLoadingData.pickup.x, self.pendingLoadingData.pickup.z); --g_additionalContractMapData:getSplineByPosition(self.pendingLoadingData.pickup.x, self.pendingLoadingData.pickup.z); --old
		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.loadingObjects:setMaxSpeed(self.objectConfig, vehicle); --replace maxspeed
				self.loadingObjects:setCanBeReset(self.objectConfig, vehicle); --replace can be reset
				self.objects[vehicle.rootNode] = vehicle;
			end;
		end;
		self:setTypDescription();
	end;	
	
	VehicleTransportMission:superClass().onSavegameLoaded(self)
end;

function VehicleTransportMission:onStartMap(args)
	
end;

function VehicleTransportMission.canRun()	
	if (g_server ~= nil and not g_additionalContractSplineManager:hasSplines()) or not g_additionalContractMapData:hasTrigger("workshop") or #VehicleTransportMission.MISSIONS_XML == 0 then 
		g_additionalContractTypes:removeBalanceTyp(VehicleTransportMission.NAME);
		g_additionalContractTypes:removeRandomTyp(VehicleTransportMission.NAME);
		g_additionalContractTypes:removeNextDayTyp(VehicleTransportMission.NAME);
		return false;
	end;
	return not g_additionalContractTypes:isMaxNextDayTyp(VehicleTransportMission.NAME) and VehicleTransportMission.NUM_INSTANCE < VehicleTransportMission.MAX_NUM_INSTANCE;
end;

function VehicleTransportMission: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 VehicleTransportMission: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 VehicleTransportMission:getActiveTriggers() --md mod
--	return self.pickup, self.dropoff, self:getTriggerTitle(self.dropoff, false);
--end;

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

function VehicleTransportMission:getNPC()	
	local npc, subData = g_additionalContractNpcManager:getNPCByName("ADC_HELPER", "vehicleWorkshop");
	if npc == nil then 
		npc = g_npcManager:getNPCByIndex(1);
	elseif subData ~= nil then
		npc.title = g_i18n:getText(subData.title);
		local imageFilename = Utils.getFilename(subData.imageFilename, AdditionalContracts.modDir);
		if imageFilename ~= nil then npc.imageFilename = imageFilename;end;
	end;
	return npc;
end;

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

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

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

function VehicleTransportMission: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 VehicleTransportMission:getOwnTableValue(key)
	if key == nil or self.data.ownTable[key] == nil then return;end;
	return self.data.ownTable[key];
end;

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

function VehicleTransportMission:init()	
	--if not VehicleTransportMission:superClass().init(self) then
	--	return false;
	--end;
	
	local transportMission = table.getRandomElement(VehicleTransportMission.MISSIONS_XML);
	if transportMission == nil then return false;end;
	local object = table.getRandomElement(transportMission.objects);
	if object == nil then return false;end;
	local dropoffTrigger = nil;
	if g_currentMission.missionDynamicInfo.isMultiplayer then dropoffTrigger = g_additionalContractMapData:getRandomTriggerNoFarmId("workshop");else dropoffTrigger = g_additionalContractMapData:getRandomTriggerEveryoneOrFarmId("workshop", g_currentMission.hlUtils.getPlayerFarmId());end;
	if dropoffTrigger == nil then return false;end;
	local distance = VehicleTransportMission.TARGET_TRIGGER_DISTANCE;
	if object.targetTriggerDistance ~= nil then distance = object.targetTriggerDistance;end;
	local pickupTrigger = g_additionalContractSplineManager:getRandomSplineByMinDistance(dropoffTrigger.x, dropoffTrigger.z, distance, object.groundType); --g_additionalContractMapData:getRandomSplineByMinDistance(dropoffTrigger.x, dropoffTrigger.z, distance, object.groundType); --old
	if pickupTrigger == nil then return false;end;	
	self.numObjects = g_additionalContractUtils:getIntegerToVector(object.minMax);	
	self.pickup = pickupTrigger;
	self.dropoff = dropoffTrigger;
	local trigger = UniversalMissionFakeTrigger.new(self.dropoff.triggerId, 1, g_dedicatedServer == nil, true);
	--trigger.index = 1;
	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(object.leasingSize, object.leasingVariant);	
	return VehicleTransportMission:superClass().init(self);		
end;

function VehicleTransportMission:setTypDescription()
	if self.objectConfig.typDescription ~= nil then
		local typDescription = g_i18n:getText(self.objectConfig.typDescription);
		if self.numObjects > 1 then 
			local typDescriptionMultiple = g_i18n:getText(self.objectConfig.typDescription.. "_multiple");
			if typDescriptionMultiple ~= nil and not string.find(typDescriptionMultiple:lower(), "missing") then typDescription = typDescriptionMultiple;end;
		end;
		self.description = typDescription;
	end;
end;

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

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

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

function VehicleTransportMission:writeStream(streamId, connection) --server
	streamWriteUInt8(streamId, self.numObjects);
	streamWriteUInt8(streamId, self.missionConfig.id);
	streamWriteUInt8(streamId, self.objectConfig.index);	
	----	
	streamWriteString(streamId, tostring(self.dropoff.triggerTyp));	
	streamWriteString(streamId, tostring(self.dropoff.uniqueId));
	
	NetworkUtil.writeNodeObject(streamId, g_currentMission.placeableSystem:getPlaceableByUniqueId(self.dropoff.uniqueId));
	
	streamWriteFloat32(streamId, self.pickup.x);
	streamWriteFloat32(streamId, self.pickup.z);	
	VehicleTransportMission:superClass().writeStream(self, streamId, connection);
end;

function VehicleTransportMission:readStream(streamId, connection) --client
	self.numObjects = streamReadUInt8(streamId);	
	self.missionConfig = self:getTransportMissionConfigById(streamReadUInt8(streamId));
	self.objectConfig = self:getTransportMissionObjectConfigByIndex(streamReadUInt8(streamId));
	self:setTypDescription();	
	----
	local dropoffTriggerTyp = streamReadString(streamId);
	local dropoffTriggerUniqueId = streamReadString(streamId);
	
	local dropoffNodeObjectId = NetworkUtil.readNodeObjectId(streamId);
	
	local pickupTriggerX = streamReadFloat32(streamId);		
	local pickupTriggerZ = streamReadFloat32(streamId);
		
	self.pendingLoadingData = {
		["dropoff"] = {uniqueId=dropoffTriggerUniqueId, nodeObjectId=dropoffNodeObjectId, triggerTyp=dropoffTriggerTyp};
		["pickup"] = {x=pickupTriggerX, z=pickupTriggerZ};
	};	
	VehicleTransportMission:superClass().readStream(self, streamId, connection);
end;

function VehicleTransportMission:triggerUpdate(trigger, dt)	
	if self.status == MissionStatus.RUNNING and trigger.triggerId ~= nil then		
		local xD, yD, zD = getWorldTranslation(trigger.triggerId);		
		for loadingNodeId, object in pairs(self.objects) do			
			if object ~= nil and entityExists(loadingNodeId) then
				local nodeId = object.rootNode or object.nodeId;
				if nodeId ~= nil and (self.objectsAtTrigger[nodeId] == nil or not self.objectsAtTrigger[nodeId]) then				
					local xV, yV, zV = getWorldTranslation(nodeId);	
					local distance = g_additionalContractUtils:getObjectDistance( {x=xD, y=yD, z=zD}, {x=xV, y=yV, z=zV} );
					if distance < 15 and yV <= yD + 0.2 then
						self.numFinished = self.numFinished + 1;
						self.objectsAtTrigger[nodeId] = true;
					end;			
				end;
			end;
		end;
	end;	
end;

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

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

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

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

function VehicleTransportMission:setLeasingVehicles(size, variant)	
	--if self.vehicleGroupIdentifier ~= nil then
	--	if variant == "NONE" then self.vehiclesToLoad = nil;self.vehicleGroupIdentifier = 1;else self.vehiclesToLoad, self.vehicleGroupIdentifier = self:getVehicleGroup();end;
	--end;	
end;

function VehicleTransportMission: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 / VehicleTransportMission.data.reward.NUM_OBJECTS_PER_DRIVE;
	return (math.ceil(driveReward) * VehicleTransportMission.data.reward.PER_METER * distance + self.numObjects * VehicleTransportMission.data.reward.PER_OBJECT) * rewardScale;	
end;

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

function VehicleTransportMission:calculateStealingCost()	
	return 0;
end;

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

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

function VehicleTransportMission: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 VehicleTransportMission: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 * VehicleTransportMission.data.ownTable.VEHICLE_USE_COST * difficultyMultiplier;
end;

function VehicleTransportMission: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 VehicleTransportMission:getDetails()	
--	local details = VehicleTransportMission:superClass().getDetails(self);
--	return details;
--end;

function VehicleTransportMission:update(dt)
	VehicleTransportMission: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 VehicleTransportMission:getMapHotspots()
	return self.mapHotspots;
end;

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

function VehicleTransportMission: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, "orangeRed");end;	
	
	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, "orangeRed");end;	
end;

function VehicleTransportMission: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 VehicleTransportMission: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 VehicleTransportMission:setPickupTrigger()
	self.pickup = {x=self.pendingLoadingData.pickup.x, z=self.pendingLoadingData.pickup.z};
	self.pendingLoadingData.pickup = nil;
end;

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

function VehicleTransportMission:setDropoffTrigger()	
	if g_currentMission.missionDynamicInfo.isMultiplayer then
		self.dropoff = g_additionalContractMapData:getTriggerByNetwork(self.pendingLoadingData.dropoff);
	else
		self.dropoff = g_additionalContractMapData:getTriggerByUniqueId(self.pendingLoadingData.dropoff.uniqueId);
	end;
	if self.dropoff ~= nil then self.pendingLoadingData.dropoff = nil;end;
end;

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

function VehicleTransportMission: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.triggerId ~= nil then x, _, z = getWorldTranslation(trigger.triggerId);elseif 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(nil, "orangeRed"); --fake for InGameMenuContractsFrame
	contractsFrameMapHotspot:setWorldPosition(x, z);
	table.addElement(self.mapHotspots, contractsFrameMapHotspot);
	return mapHotspot;
end;

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

function VehicleTransportMission:loadObjects()
	local trigger = self:getPickupTrigger();
	local objectConfig = self.objectConfig;	
	if objectConfig == nil then
		return false;
	end;
	local canSpawn, objectsSpawnPosition = g_additionalContractSpawnManager:getObjectsSpawnPosition(trigger, nil, self.numObjects, objectConfig); --g_additionalContractUtils:getObjectsSpawnPosition(trigger, nil, self.numObjects, objectConfig); --old
	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 VehicleTransportMission:loadInit()
	g_additionalContractTypes:insertRandomTyp(VehicleTransportMission.NAME);
	g_additionalContractTypes:insertBalanceTyp(VehicleTransportMission.NAME);
	g_additionalContractTypes:insertNextDayTyp(VehicleTransportMission.NAME, VehicleTransportMission.MAX_NUM_DAY);	
	local xmlFilename = Utils.getFilename("missions/universalMission/vehicleTransportMission/missions.xml", AdditionalContracts.modDir);
	if xmlFilename ~= nil and xmlFilename ~= "" then VehicleTransportMission.MISSIONS_XML = g_additionalContractUtils:loadMissionsXml(xmlFilename);end;
	xmlFilename = Utils.getFilename("missionVehicles/vehicleTransportMissionVehicles.xml", AdditionalContracts.modDir)
	if xmlFilename ~= nil and xmlFilename ~= "" then g_missionManager:addPendingMissionVehiclesFile(xmlFilename, AdditionalContracts.modDir);end;
end;

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

function VehicleTransportMission:getClassTypOverlay(args)
	local classOverlay = "tractor";
	local typOverlay = "unknownTrailer";
	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;

---balance system---

---balance system---

g_additionalContractTypes:registerTyp(VehicleTransportMission, VehicleTransportMission.NAME);