Code: Select all
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.
BunkerSilo = {};
BunkerSilo_mt = Class(BunkerSilo, Placeable);
InitObjectClass(BunkerSilo, "BunkerSilo");
function BunkerSilo:onCreate(id)
local object = BunkerSilo:new(g_server ~= nil, g_client ~= nil);
if object:load(id) then
g_currentMission:addOnCreateLoadedObject(object);
g_currentMission:addOnCreateLoadedObjectToSave(object);
object:register(true);
else
object:delete();
end;
end;
function BunkerSilo:new(isServer, isClient, customMt)
local mt = customMt;
if mt == nil then
mt = BunkerSilo_mt;
end;
local self = Object:new(isServer, isClient, mt);
self.nodeId = 0;
self.acceptedFillTypes = {};
self.inputFillType = FillUtil.FILLTYPE_CHAFF;
self.fermentingFillType = FillUtil.FILLTYPE_TARP;
self.outputFillType = FillUtil.FILLTYPE_SILAGE;
self.fillLevel = 0;
self.compactedFillLevel = 0;
self.compactedPercent = 0;
self.emptyThreshold = 100;
self.fermentingTime = 0;
self.fermentingDuration = 6*60*60; -- 6hours (ingame)
self.fermentingPercent = 0;
self.isOpenedAtFront = false;
self.isOpenedAtBack = false;
self.distanceToCompactedFillLevel = 100;
self.interactionTriggerId = nil;
self.playerInRange = false;
self.vehiclesInRange = {};
self.numVehiclesInRange = 0;
self.bunkerSiloArea = {};
self.bunkerSiloArea.offsetFront = 0;
self.bunkerSiloArea.offsetBack = 0
self.siloIsFullWarningTimer = 0;
self.siloIsFullWarningDuration = 2000;
self.activatable = BunkerSiloActivatable:new(self);
self.state = BunkerSilo.STATE_FILL;
self.bunkerSiloDirtyFlag = self:getNextDirtyFlag();
return self;
end;
function BunkerSilo:delete()
g_currentMission:removeOnCreateLoadedObjectToSave(self);
if self.interactionTriggerId ~= nil then
removeTrigger(self.interactionTriggerId);
end;
if self.nodeId ~= 0 then
g_currentMission:removeNodeObject(self.nodeId);
end;
g_currentMission:removeActivatableObject(self.activatable);
BunkerSilo:superClass().delete(self);
end;
function BunkerSilo:readStream(streamId, connection)
BunkerSilo:superClass().readStream(self, streamId, connection);
if connection:getIsServer() then
local state = streamReadUIntN(streamId, 3);
self:setState(state);
self.isOpenedAtFront = streamReadBool(streamId);
self.isOpenedAtBack = streamReadBool(streamId);
self.fillLevel = streamReadFloat32(streamId);
self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
end;
end;
function BunkerSilo:writeStream(streamId, connection)
BunkerSilo:superClass().writeStream(self, streamId, connection);
if not connection:getIsServer() then
streamWriteUIntN(streamId, self.state, 3);
streamWriteBool(streamId, self.isOpenedAtFront);
streamWriteBool(streamId, self.isOpenedAtBack);
streamWriteFloat32(streamId, self.fillLevel);
streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8);
streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8);
end;
end;
function BunkerSilo:readUpdateStream(streamId, timestamp, connection)
BunkerSilo:superClass().readUpdateStream(self, streamId, timestamp, connection)
if connection:getIsServer() then
if streamReadBool(streamId) then
local state = streamReadUIntN(streamId, 3);
if state ~= self.state then
self:setState(state, true);
end
self.fillLevel = streamReadFloat32(streamId);
self.isOpenedAtFront = streamReadBool(streamId);
self.isOpenedAtBack = streamReadBool(streamId);
if self.state == BunkerSilo.STATE_FILL then
self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5);
end
end
end;
end;
function BunkerSilo:writeUpdateStream(streamId, connection, dirtyMask)
BunkerSilo:superClass().writeUpdateStream(self, streamId, connection, dirtyMask)
if not connection:getIsServer() then
if streamWriteBool(streamId, bitAND(dirtyMask, self.bunkerSiloDirtyFlag) ~= 0) then
streamWriteUIntN(streamId, self.state, 3);
streamWriteFloat32(streamId, self.fillLevel);
streamWriteBool(streamId, self.isOpenedAtFront);
streamWriteBool(streamId, self.isOpenedAtBack);
if self.state == BunkerSilo.STATE_FILL then
streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8);
elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8);
end
end
end;
end;
function BunkerSilo:load(nodeId)
self.nodeId = nodeId;
local areaNode = Utils.indexToObject(nodeId, getUserAttribute(nodeId, "bunkerSiloArea"));
if areaNode == nil then
print("Warning: BunkerSilo, could not resolve attribute 'bunkerSiloArea' into a valid node!");
return false;
end
self.bunkerSiloArea.start = getChildAt(areaNode, 0);
self.bunkerSiloArea.width = getChildAt(areaNode, 1);
self.bunkerSiloArea.height = getChildAt(areaNode, 2);
self.bunkerSiloArea.sx, self.bunkerSiloArea.sy, self.bunkerSiloArea.sz = getWorldTranslation(self.bunkerSiloArea.start);
self.bunkerSiloArea.wx, self.bunkerSiloArea.wy, self.bunkerSiloArea.wz = getWorldTranslation(self.bunkerSiloArea.width);
self.bunkerSiloArea.hx, self.bunkerSiloArea.hy, self.bunkerSiloArea.hz = getWorldTranslation(self.bunkerSiloArea.height);
self.bunkerSiloArea.dhx = self.bunkerSiloArea.hx - self.bunkerSiloArea.sx;
self.bunkerSiloArea.dhy = self.bunkerSiloArea.hy - self.bunkerSiloArea.sy;
self.bunkerSiloArea.dhz = self.bunkerSiloArea.hz - self.bunkerSiloArea.sz;
local dhLength = Utils.vector3Length(self.bunkerSiloArea.dhx, self.bunkerSiloArea.dhy, self.bunkerSiloArea.dhz);
self.bunkerSiloArea.dhx_norm = self.bunkerSiloArea.dhx / dhLength;
self.bunkerSiloArea.dhy_norm = self.bunkerSiloArea.dhy / dhLength;
self.bunkerSiloArea.dhz_norm = self.bunkerSiloArea.dhz / dhLength;
self.bunkerSiloArea.dwx = self.bunkerSiloArea.wx - self.bunkerSiloArea.sx;
self.bunkerSiloArea.dwy = self.bunkerSiloArea.wy - self.bunkerSiloArea.sy;
self.bunkerSiloArea.dwz = self.bunkerSiloArea.wz - self.bunkerSiloArea.sz;
local dwLength = Utils.vector3Length(self.bunkerSiloArea.dwx, self.bunkerSiloArea.dwy, self.bunkerSiloArea.dwz);
self.bunkerSiloArea.dwx_norm = self.bunkerSiloArea.dwx / dwLength;
self.bunkerSiloArea.dwy_norm = self.bunkerSiloArea.dwy / dwLength;
self.bunkerSiloArea.dwz_norm = self.bunkerSiloArea.dwz / dwLength;
self.acceptedFillTypes = {};
local dataString = Utils.getNoNil(getUserAttribute(nodeId, "acceptedFillTypes"), "chaff grass_windrow dryGrass_windrow");
local data = Utils.splitString(" ", dataString);
for i=1,table.getn(data) do
local fillType = FillUtil.fillTypeNameToInt[data[i] ];
if fillType ~= nil then
self.acceptedFillTypes[fillType] = true;
else
print("Warning: BunkerSilo has an invalid fillType (acceptedFillTypes): "..tostring(data[i]));
end
end
local dataString = Utils.getNoNil(getUserAttribute(nodeId, "inputFillType"), "chaff");
local fillType = FillUtil.fillTypeNameToInt[dataString];
if fillType ~= nil then
self.inputFillType = fillType;
else
print("Warning: BunkerSilo has an invalid fillType (inputFillType): "..tostring(dataString));
end
local dataString = Utils.getNoNil(getUserAttribute(nodeId, "outputFillType"), "silage");
local fillType = FillUtil.fillTypeNameToInt[dataString];
if fillType ~= nil then
self.outputFillType = fillType;
else
print("Warning: BunkerSilo has an invalid fillType (outputFillType): "..tostring(dataString));
end
if self.isServer then
TipUtil.setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType);
end
local distanceToCompactedFillLevelStr = getUserAttribute(nodeId, "distanceToCompactedFillLevel");
if distanceToCompactedFillLevelStr ~= nil then
self.distanceToCompactedFillLevel = Utils.getNoNil(tonumber(distanceToCompactedFillLevelStr), self.distanceToCompactedFillLevel);
end;
local fermentingDurationStr = getUserAttribute(nodeId, "fermentingDuration");
if fermentingDurationStr ~= nil then
self.fermentingDuration = Utils.getNoNil(tonumber(fermentingDurationStr), self.fermentingDuration);
end;
self.fillLevel = 0;
local interactionTriggerIndex = getUserAttribute(nodeId, "interactionTriggerIndex");
if interactionTriggerIndex ~= nil then
self.interactionTriggerId = Utils.indexToObject(nodeId, interactionTriggerIndex);
if self.interactionTriggerId ~= nil then
addTrigger(self.interactionTriggerId, "interactionTriggerCallback", self);
end;
end;
self.openingLength = Utils.getNoNil(getUserAttribute(nodeId, "openingLength"), 5);
-- adjust timings to difficulty
local difficultyMultiplier = g_currentMission.missionInfo.difficulty;
self.fermentingDuration = self.fermentingDuration*difficultyMultiplier;
self.distanceToCompactedFillLevel = self.distanceToCompactedFillLevel/difficultyMultiplier;
-- just for tutorial 12 (feeder)
self.isTutorialSilo = Utils.getNoNil( getUserAttribute(nodeId, "isTutorialSilo"), false );
self.saveId = Utils.getNoNil( getUserAttribute(nodeId, "saveId"), "BunkerSilo_"..getName(nodeId) );
g_currentMission:addNodeObject(self.nodeId, self);
self:setState(BunkerSilo.STATE_FILL);
return true;
end;
function BunkerSilo:loadFromAttributesAndNodes(xmlFile, key)
local state = getXMLInt(xmlFile, key.."#state");
if state ~= nil then
if state >= 0 and state < BunkerSilo.NUM_STATES then
self:setState(state);
end;
end;
local fillLevel = getXMLFloat(xmlFile, key.."#fillLevel");
if fillLevel ~= nil then
self.fillLevel = fillLevel;
end;
local compactedFillLevel = getXMLFloat(xmlFile, key.."#compactedFillLevel");
if compactedFillLevel ~= nil then
self.compactedFillLevel = Utils.clamp(compactedFillLevel, 0, self.fillLevel);
end;
local fermentingTime = getXMLFloat(xmlFile, key.."#fermentingTime");
if fermentingTime ~= nil then
self.fermentingTime = Utils.clamp(fermentingTime, 0, self.fermentingDuration);
end;
local fermentingPercent = getXMLFloat(xmlFile, key.."#fermentingPercent");
if fermentingPercent ~= nil then
self.fermentingPercent = Utils.clamp(fermentingPercent, 0, 100);
end;
self.isOpenedAtFront = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtFront"), false);
self.isOpenedAtBack = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtBack"), false);
if self.isOpenedAtFront then
self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.outputFillType);
else
self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType);
end
if self.isOpenedAtBack then
self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.outputFillType);
else
self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(false, 0, self.fermentingFillType);
end
return true;
end;
function BunkerSilo:getSaveAttributesAndNodes(nodeIdent)
local attributes = 'state="'..self.state..'" fillLevel="'..self.fillLevel..'" compactedFillLevel="'..self.compactedFillLevel..'"';
attributes = attributes .. ' fermentingTime="'..self.fermentingTime..'" fermentingPercent="'..self.fermentingPercent..'"';
attributes = attributes .. ' openedAtFront="' .. tostring(self.isOpenedAtFront) ..'" openedAtBack="' .. tostring(self.isOpenedAtBack) .. '"';
local nodes = "";
return attributes, nodes;
end;
function BunkerSilo:update(dt)
if self:getCanInteract(true) then
local fillType = self.inputFillType;
if self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN then
fillType = self.outputFillType;
end
local fillTypeName = "";
if FillUtil.fillTypeIndexToDesc[fillType] ~= nil then
fillTypeName = FillUtil.fillTypeIndexToDesc[fillType].nameI18N;
end
if self.state == BunkerSilo.STATE_FILL then
g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel));
g_currentMission:addExtraPrintText(g_i18n:getText("info_compacting")..string.format(" %d%%", self.compactedPercent));
elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then
g_currentMission:addExtraPrintText(g_i18n:getText("info_fermenting")..string.format(" %s: %d%%", fillTypeName, self.fermentingPercent));
elseif self.state == BunkerSilo.STATE_DRAIN then
g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel));
end;
end;
if self.state == BunkerSilo.STATE_CLOSED then
if self.isServer then
self.fermentingTime = math.min(self.fermentingDuration, self.fermentingTime + dt*0.001*g_currentMission.missionInfo.timeScale);
local fermentingPercent = Utils.getFlooredPercent(self.fermentingTime, self.fermentingDuration);
if fermentingPercent ~= self.fermentingPercent then
self.fermentingPercent = fermentingPercent;
self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
end
if self.fermentingTime >= self.fermentingDuration then
self:setState(BunkerSilo.STATE_FERMENTED, true);
end;
end;
end;
if self.isServer then
if self.state == BunkerSilo.STATE_FILL then
for vehicle,state in pairs(self.vehiclesInRange) do
if state then
if vehicle:getIsActive() then
local distance = vehicle.lastMovedDistance;
if distance > 0 then
local mass = vehicle:getTotalMass(false);
local refNode = vehicle.bunkerSiloCompactingRefNode;
if refNode == nil then
refNode = vehicle.components[1].node;
end
local scale = (mass / BunkerSilo.COMPACTING_BASE_MASS) * Utils.getNoNil(vehicle.bunkerSiloCompactingScale, 1);
local deltaCompact = distance*scale*self.distanceToCompactedFillLevel;
local numWheels = table.getn(vehicle.wheels);
if numWheels > 0 then
local activeWheels = 0;
for i=1,numWheels do
local wheel = vehicle.wheels[i];
if wheel.contact == Vehicle.WHEEL_GROUND_HEIGHT_CONTACT then
activeWheels = activeWheels + 1;
end
end
deltaCompact = deltaCompact * (activeWheels / numWheels);
end
local compactedFillLevel = math.min(self.compactedFillLevel + deltaCompact, self.fillLevel);
if compactedFillLevel ~= self.compactedFillLevel then
self.compactedFillLevel = compactedFillLevel;
self.compactedPercent = Utils.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel);
self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
end
end;
end;
end
end;
end;
end;
-- for chaff tutorial: always take the highest fill level of all bunker silos
if g_currentMission ~= nil and g_currentMission.bunkerScore ~= nil then
if g_currentMission.bunkerScore < self.fillLevel then
g_currentMission.bunkerScore = self.fillLevel;
end;
end;
end;
function BunkerSilo:updateTick(dt)
if self.isServer then
local dirty = false;
local area = self.bunkerSiloArea;
local fillLevel = self.fillLevel;
local fillType = self.inputFillType;
if self.inputFillType == FillUtil.FILLTYPE_UNKNOWN then
elseif self.state == BunkerSilo.STATE_FILL then
fillLevel = TipUtil.getFillLevelAtArea(fillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz);
elseif self.state == BunkerSilo.STATE_CLOSED then
fillLevel = TipUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
elseif self.state == BunkerSilo.STATE_FERMENTED then
fillLevel1 = TipUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
fillLevel2 = TipUtil.getFillLevelAtArea(self.outputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
fillLevel = fillLevel1 + fillLevel2;
elseif self.state == BunkerSilo.STATE_DRAIN then
fillLevel1 = TipUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
fillLevel2 = TipUtil.getFillLevelAtArea(self.outputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz)
fillLevel = fillLevel1 + fillLevel2;
if fillLevel < self.emptyThreshold then
TipUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.fermentingFillType);
TipUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.outputFillType);
self:setState(BunkerSilo.STATE_FILL, true);
end
end
dirty = fillLevel ~= self.fillLevel;
if dirty then
self.fillLevel = fillLevel;
self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
end;
end;
end;
function BunkerSilo:setState(state, showNotification)
if state ~= self.state then
if state == BunkerSilo.STATE_FILL then
self.fermentingTime = 0;
self.compactedFillLevel = 0;
self.isOpenedAtFront = false;
self.isOpenedAtBack = false;
self.bunkerSiloArea.offsetFront = 0;
self.bunkerSiloArea.offsetBack = 0;
if showNotification then
g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloIsEmpty"));
end
if self.isServer then
TipUtil.removeFixedFillTypesArea(self.bunkerSiloArea);
TipUtil.setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType);
end
elseif state == BunkerSilo.STATE_CLOSED then
if self.isServer then
-- change fillType
local area = self.bunkerSiloArea;
local offsetFront = self:getBunkerAreaOffset(true, 0, self.inputFillType);
local offsetBack = self:getBunkerAreaOffset(false, 0, self.inputFillType);
local x0 = area.sx + (offsetFront * area.dhx_norm);
local z0 = area.sz + (offsetFront * area.dhz_norm);
local x1 = x0 + area.dwx;
local z1 = z0 + area.dwz;
local x2 = area.sx + area.dhx - (offsetBack * area.dhx_norm);
local z2 = area.sz + area.dhz - (offsetBack * area.dhz_norm);
local changed = TipUtil.changeFillTypeAtArea(x0,z0, x1,z1, x2,z2, self.inputFillType, self.fermentingFillType);
if self.isServer then
TipUtil.removeFixedFillTypesArea(self.bunkerSiloArea);
TipUtil.removeConvertingFillTypeAreas(self.bunkerSiloArea);
end
end
if showNotification then
g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloCovered"));
end
elseif state == BunkerSilo.STATE_FERMENTED then
if showNotification then
g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloDoneFermenting"));
end
elseif state == BunkerSilo.STATE_DRAIN then
self.bunkerSiloArea.offsetFront = 0;
self.bunkerSiloArea.offsetBack = 0;
if showNotification then
g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloOpened"));
end
if self.isServer then
TipUtil.removeConvertingFillTypeAreas(self.bunkerSiloArea);
local fillTypes = {};
fillTypes[self.outputFillType] = true;
TipUtil.setFixedFillTypesArea(self.bunkerSiloArea, fillTypes);
end
end;
self.state = state;
if self.isServer then
self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
end;
end;
end;
function BunkerSilo:openSilo(px,py,pz)
self:setState(BunkerSilo.STATE_DRAIN, true);
self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType);
self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType);
-- check which side is closer to player
local openedAtFront = self.isOpenedAtFront;
local openedAtBack = self.isOpenedAtBack;
local openAtFront = self:getIsCloserToFront(px,py,pz);
if openAtFront and not self.isOpenedAtFront then
self:switchFillTypeAtOffset(true, self.bunkerSiloArea.offsetFront, self.openingLength);
self.isOpenedAtFront = true;
self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
elseif not self.isOpenedAtBack then
self:switchFillTypeAtOffset(false, self.bunkerSiloArea.offsetBack, self.openingLength);
self.isOpenedAtBack = true;
self:raiseDirtyFlags(self.bunkerSiloDirtyFlag);
end;
end;
function BunkerSilo:getBunkerAreaOffset(updateAtFront, offset, fillType)
local area = self.bunkerSiloArea;
local hx, hy, hz = area.dhx_norm, area.dhy_norm, area.dhz_norm;
if not updateAtFront then
hx, hy, hz = -area.dhx_norm, -area.dhy_norm, -area.dhz_norm;
end
local hl = Utils.vector3Length(area.dhx, area.dhy, area.dhz);
local a0x, a0z = nil, nil;
local a1x, a1z = nil, nil;
local a2x, a2z = nil, nil;
local i = offset;
while i < (hl - 1) do
local d1x,d1y,d1z = i*hx, i*hy, i*hz;
local d2x,d2y,d2z = (i+1)*hx, (i+1)*hy, (i+1)*hz;
if updateAtFront then
a0x, a0z = area.sx + d1x, area.sz + d1z;
a1x, a1z = area.wx + d1x, area.wz + d1z;
a2x, a2z = area.sx + d2x, area.sz + d2z;
else
a0x, a0z = area.hx + d1x, area.hz + d1z;
a1x, a1z = area.hx + d1x + area.dwx, area.hz + d1z + area.dwz;
a2x, a2z = area.hx + d2x, area.hz + d2z;
end
local fillLevel = TipUtil.getFillLevelAtArea(fillType, a0x,a0z, a1x,a1z, a2x,a2z);
if fillLevel > 0 then
offset = i;
break;
end
i = i + 1;
end
return offset;
end
function BunkerSilo:switchFillTypeAtOffset(switchAtFront, offset, length)
local fillType = self.fermentingFillType;
local newFillType = self.outputFillType;
local a0x, a0z = nil, nil;
local a1x, a1z = nil, nil;
local a2x, a2z = nil, nil;
local area = self.bunkerSiloArea;
if switchAtFront then
a0x, a0z = area.sx + (offset * area.dhx_norm), area.sz + (offset * area.dhz_norm);
a1x, a1z = a0x + area.dwx, a0z + area.dwz;
a2x, a2z = area.sx + ((offset + length) * area.dhx_norm), area.sz + ((offset + length) * area.dhz_norm);
else
a0x, a0z = area.hx - (offset * area.dhx_norm), area.hz - (offset * area.dhz_norm);
a1x, a1z = a0x + area.dwx, a0z + area.dwz;
a2x, a2z = area.hx - ((offset + length) * area.dhx_norm), area.hz - ((offset + length) * area.dhz_norm);
end
TipUtil.changeFillTypeAtArea(a0x,a0z, a1x,a1z, a2x,a2z, fillType, newFillType);
if switchAtFront then
TipUtil.removeEmptyFillTypeAtArea(area.sx,area.sz, area.wx,area.wz, a2x,a2z, fillType);
else
TipUtil.removeEmptyFillTypeAtArea(area.hx,area.hz, area.hx+area.dwx,area.hz+area.dwz, a2x,a2z, fillType);
end
end
function BunkerSilo:getIsCloserToFront(ix,iy,iz)
local area = self.bunkerSiloArea;
local x = area.sx + (0.5*area.dwx) + (area.offsetFront * area.dhx_norm);
local y = area.sy + (0.5*area.dwy) + (area.offsetFront * area.dhy_norm);
local z = area.sz + (0.5*area.dwz) + (area.offsetFront * area.dhz_norm);
local distFront = Utils.vector3Length(x-ix, y-iy, z-iz);
local x = area.sx + (0.5*area.dwx) + area.dhx - (area.offsetBack * area.dhx_norm);
local y = area.sy + (0.5*area.dwy) + area.dhy - (area.offsetBack * area.dhy_norm);
local z = area.sz + (0.5*area.dwz) + area.dhz - (area.offsetBack * area.dhz_norm);
local distBack = Utils.vector3Length(x-ix, y-iy, z-iz);
return distFront < distBack;
end
function BunkerSilo:getCanInteract(showInformationOnly)
if showInformationOnly then
if (g_currentMission.controlPlayer and self.playerInRange) then
return true;
end;
if not g_currentMission.controlPlayer then
for vehicle in pairs(self.vehiclesInRange) do
if vehicle:getIsActiveForInput(false) then
return true;
end;
end;
end;
else
if (g_currentMission.controlPlayer and self.playerInRange) then
--if next(self.vehiclesInRange) == nil then
return true;
--end
end;
end
return false;
end;
function BunkerSilo:getCanCloseSilo()
return self.state == BunkerSilo.STATE_FILL and self.fillLevel > 0 and self.compactedPercent >= 100;
end;
function BunkerSilo:getCanOpenSilo()
if not (self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN) then
return false;
end
local ix,iy,iz = self:getInteractionPosition();
if ix ~= nil then
local closerToFront = self:getIsCloserToFront(ix,iy,iz);
if closerToFront and not self.isOpenedAtFront then
return true;
end
if not closerToFront and not self.isOpenedAtBack then
return true;
end
end
return false;
end
function BunkerSilo:getInteractionPosition()
if g_currentMission.controlPlayer and self.playerInRange then
return getWorldTranslation(g_currentMission.player.rootNode);
else
if self.vehiclesInRange[g_currentMission.currentVehicle] ~= nil then
return getWorldTranslation(self.vehiclesInRange[g_currentMission.currentVehicle].components[1].node);
end
end
return nil;
end
function BunkerSilo:interactionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId)
if onEnter or onLeave then
if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then
if onEnter then
self.playerInRange = true;
g_currentMission:removeActivatableObject(self.activatable); -- make sure it is not added twice
g_currentMission:addActivatableObject(self.activatable);
else
self.playerInRange = false;
if self.numVehiclesInRange == 0 then
g_currentMission:removeActivatableObject(self.activatable);
end;
end;
else
--local vehicle = g_currentMission.nodeToVehicle[otherId];
local vehicle = g_currentMission.nodeToVehicle[otherShapeId];
if vehicle ~= nil then
if onEnter then
if self.vehiclesInRange[vehicle] == nil then
self.vehiclesInRange[vehicle] = true;
self.numVehiclesInRange = self.numVehiclesInRange + 1;
g_currentMission:removeActivatableObject(self.activatable); -- make sure it is not added twice
g_currentMission:addActivatableObject(self.activatable);
-- add callback if shovel
if vehicle.setChangedFillLevelCallback ~= nil then
vehicle:setChangedFillLevelCallback(BunkerSilo.onChangedFillLevelCallback, self);
end
end;
else
if self.vehiclesInRange[vehicle] then
self.vehiclesInRange[vehicle] = nil;
self.numVehiclesInRange = self.numVehiclesInRange - 1;
if self.numVehiclesInRange == 0 and not self.playerInRange then
g_currentMission:removeActivatableObject(self.activatable);
end;
-- remove callback if shovel
if vehicle.setChangedFillLevelCallback ~= nil then
vehicle:setChangedFillLevelCallback(nil);
end
end;
end;
end;
end;
end;
end;
function BunkerSilo.onChangedFillLevelCallback(self, shovel, fillDelta, fillType)
if fillDelta >= 0 then
return;
end
local area = self.bunkerSiloArea;
local x,y,z;
if shovel.pickUp.node ~= nil then
x,y,z = localToWorld(shovel.pickUp.node, 0, 0, shovel.pickUp.length);
else
x,y,z = getWorldTranslation(shovel.components[1].node);
end;
local closerToFront = self:getIsCloserToFront(x,y,z);
local length = self.openingLength;
if closerToFront then
if self.isOpenedAtFront then
local p1 = Utils.getProjectOnLineParameter(x,z, area.sx,area.sz, area.dhx_norm,area.dhz_norm);
p1 = math.floor(p1 + 0.5);
if p1 > area.offsetFront - length then
local offset = self:getBunkerAreaOffset(true, area.offsetFront, self.fermentingFillType);
local targetOffset = math.max(p1 + length, offset);
local length = targetOffset - area.offsetFront;
self:switchFillTypeAtOffset(true, area.offsetFront, length);
area.offsetFront = area.offsetFront + length;
end
end
else
if self.isOpenedAtBack then
local p1 = Utils.getProjectOnLineParameter(x,z, area.hx,area.hz, -area.dhx_norm,-area.dhz_norm);
p1 = math.floor(p1 + 0.5);
if p1 > area.offsetBack - length then
local offset = self:getBunkerAreaOffset(true, area.offsetBack, self.fermentingFillType);
local targetOffset = math.max(p1 + length, offset);
local length = targetOffset - area.offsetBack;
self:switchFillTypeAtOffset(false, area.offsetBack, length);
area.offsetBack = area.offsetBack + length;
end
end
end
end
registerPlaceableType("BunkerSilo", BunkerSilo);