How to add Phase shields to a unit and create custom unit meshes

I took something from the old DMS files and modified it to create a custom phase mesh for any unit with "Phasing" in its unit blue print.

Snippet from my custom hook'd BluePrint.lua

function Custom_4DFAF_ExtractPhaseMeshBlueprint(bp)

	--Recover the mesh ID or return
	local meshid = bp.Display.MeshBlueprint
	if not meshid then return end
	
	--Pull the original unit blueprints by its mesh ID
	local meshbp = original_blueprints.Mesh[meshid]
	if not meshbp then return end
	     
	--Verify the unit type is one that we are allowed to enhance with Phasing   
	if not table.find(bp.Categories, 'PHASING') then
		return
	else	 

		local LODCutoff, LookupName, NormalsName, SecondaryName, ShaderName, SpecularName
		
		--Custom mesh modifications
		LODCutoff = 195
		LookupName = '/effects/entities/PhaseShield/PhaseShieldLookup.dds'
		NormalsName = '/mods/4DFAF/lua/CustomAbilities/4D_UnitPhasing/4D_Phase_Texture.dds'              
		ShaderName = 'ShieldSeraphim'                       
		SpecularName = '/mods/4DFAF/lua/CustomAbilities/4D_UnitPhasing/4D_Phase_Texture.dds'  
		
		--Create the new mesh						
		local phasemeshbp = table.deepcopy(meshbp)
		phasemeshbp.Merge = true
		if phasemeshbp.LODs then
			for i,lod in phasemeshbp.LODs do				
				lod.LookupName = LookupName
				lod.NormalsName = NormalsName
				lod.ShaderName = ShaderName
				lod.SpecularName = SpecularName
				lod.LODCutoff = LODCutoff
			end
		end
		
		--Save the new mesh
		phasemeshbp.BlueprintId = meshid .. '_phaseshield'
		bp.Display.PhaseMeshBlueprint = phasemeshbp.BlueprintId
		MeshBlueprint(phasemeshbp)		
	end
end

The above Script called for the following textures...
4D_Phase_Texture.zip

My unit blue print has the following...

	Categories = {
		'MODCONTENT4D',
		'AIR',
		'DIRECTFIRE',
		'MOBILE',
		'OVERLAYANTINAVY',
		'OVERLAYDIRECTFIRE',
		'PHASING',
		'RECLAIMABLE',
		'SELECTABLE',
		'SERAPHIM',
		'TECH3',
		'VISIBLETORECON',
	},

	Phasing = {
		PhasePercent = 35,
		StartEnabled = true,
	},

The unit enables the phase shield via toggle bit "7" or via the following function...

self:ForkThread(self.PhaseEnable)

Which in turn runs... /mods/4DFAF/lua/CustomAbilities/4D_UnitPhasing/4D_UnitPhasing.lua

-----------------------------------------------------------------------------
--	File		  : /lua/UnitPhasing.lua
--
--	Author		  : Resin_Smoker
--
--	Summary 	  : Unit Phasing: Allows a unit so enhanced to shift it's molecules
--					around into extra spatial dimensions becoming translucent and
--					semi-ethereal, almost completely non corporeal.
--					The advantage to this is that a unit so enhanced can avoid a
--					percentage direct fire weapons by allowing them to pass thru it
--					harmlessly! While protected from directfire projectiles, a Phased
--					unit is still very vunerable from laser weapons, damage over time
--					weapons, and projectile with signifigant splash damage.
--
--	Copyright © 2024 4DFAF, All rights reserved.
-----------------------------------------------------------------------------
--
--	The following is required in the units script for UnitPhasing
--	This calls into action the UnitPhasing scripts for SAirUnit
--
--	local SAirUnit = import('/lua/seraphimunits.lua').SAirUnit
--	SAirUnit = import('/mods/4DFAF/lua/CustomAbilities/4D_UnitPhasing/4D_UnitPhasing.lua').UnitPhasing( SAirUnit )
--
-----------------------------------------------------------------------------
		
local myDebug = true
		
-- Start of UnitPhasing(SuperClass)
function UnitPhasing(SuperClass)
	return Class(SuperClass) {
	
	OnStopBeingBuilt = function(self, builder, layer)
		SuperClass.OnStopBeingBuilt(self, builder, layer)
		
		if myDebug then WARN('UnitPhasing OnStopBeingBuilt') end
		
		--Phase Global Variables
		self.MyBp = self:GetBlueprint()
		self.PhaseBp = self.MyBp.Phasing
		if myDebug then WARN('	PhaseBP = '..repr(self.PhaseBp)) end
		self.OldPhaseImpact = nil
		self.PhaseAbilityEnabled = false
		self.PriorPhaseStatus = false
		self:SetMaintenanceConsumptionInactive()
		
		--Enable Phasing if StartsOn true in BP
		if self.PhaseBp.StartEnabled == true then
			self:SetScriptBit('RULEUTC_SpecialToggle', true)
		end
		--self:ForkThread(self.PhaseEconomyHeartBeat)
	end,

	PhaseEconomyHeartBeat = function(self)
		local cost = self.MyBp.Economy.MaintenanceConsumptionPerSecondEnergy
		while self and not ( self:IsDead() or self:BeenDestroyed() ) do
		
			--Verify that economy is capabile of supporting phasing and that the unit isnt in transport
			local econ = self:GetAIBrain():GetEconomyStored('Energy')
			if (econ < cost and self.PhaseAbilityEnabled == true and self.PriorPhaseStatus == false) or ((self:IsUnitState('Attached') or self:IsUnitState('TransportLoading')) and (self.PhaseAbilityEnabled == true and self.PriorPhaseStatus == false)) then
				self.PriorPhaseStatus = true
				self:ForkThread(self.PhaseDisable)
			elseif (econ > cost and self.PriorPhaseStatus == true and self.PhaseAbilityEnabled == false) and (not self:IsUnitState('Attached') and not self:IsUnitState('TransportUnloading') and not self:IsUnitState('TransportLoading')) then
				self.PriorPhaseStatus = false
				self:ForkThread(self.PhaseEnable)
			end
			
			--Short delay between checks
			WaitTicks(Random(2, 4))
		end
	end,

	OnScriptBitSet = function(self, bit)
		if bit == 7 then
		
			if myDebug then WARN('OnScriptBitSet: ', bit)end
			
			self:ForkThread(self.PhaseEnable)
		end
		SuperClass.OnScriptBitSet(self, bit)
	end,

	OnScriptBitClear = function(self, bit)	
		if bit == 7 then
			if self.PriorPhaseStatus == true then
				self.PriorPhaseStatus = false
			end
		   
			if myDebug then WARN('OnScriptBitClear: ', bit, '	Prior Status: ', self.PriorPhaseStatus) end
			
			self:ForkThread(self.PhaseDisable)
		end
		SuperClass.OnScriptBitClear(self, bit)
	end,

	PhaseEnable = function(self)
		WaitSeconds(1)
		if self and not ( self:IsDead() or self:BeenDestroyed() ) then
		
			if myDebug then WARN('PhaseEnable called') end
			
			if self.PhaseAbilityEnabled == false and not self:IsUnitState('Attached') then
			
				if myDebug then WARN('	Adding mesh and economy') end
				
				self:SetMesh(self.MyBp.Display.PhaseMeshBlueprint, true)
				self.PhaseAbilityEnabled = true
				self:SetMaintenanceConsumptionActive()
			end
		end
	end,

	PhaseDisable = function(self)
		if self and not ( self:IsDead() or self:BeenDestroyed() ) then
			if myDebug then WARN('PhaseDisable called') end
			
			if self.PhaseAbilityEnabled == true then
			
				if myDebug then WARN('	Removing mesh and economy') end
							
				self.PhaseAbilityEnabled = false
				self:SetMaintenanceConsumptionInactive()
				self:SetMesh(self.MyBp.Display.MeshBlueprint, true)
			end
		end
	end,

	OnCollisionCheck = function(self, other, firingWeapon)
		if self and not ( self:IsDead() or self:BeenDestroyed() ) then
		
			if myDebug then WARN('OnCollisionCheck, % chance of Phasing = ', self.PhaseBp.PhasePercent) end
			
			if self.PhaseAbilityEnabled then
				if EntityCategoryContains(categories.PROJECTILE, other) then
				
					--Process phase chance, allows % of projectiles to pass 
					if myDebug then WARN(' Hit by projectile') end
					local ran = Random(1,100)
					if ran <= self.PhaseBp.PhasePercent or other == self.OldPhaseImpact then
						
						--Remember what last impacted to prevent the same projectile from being checked twice
						self.OldPhaseImpact = other
						
						--Returning false allows the projectile to pass thru
						if myDebug then WARN('	Passing projectile through') end
						return false
					else
					
						--Projectile impacts normally
						if myDebug then WARN('	Normal impact') end
						self.OldPhaseImpact = nil
						return true
					end
				end
			end
			return SuperClass.OnCollisionCheck(self, other, firingWeapon)
		end
	end,
}
end
-- End of UnitPhasing(UnitPhasing)

With the resulting unit looking like this..
8c390484-b466-4fab-a05f-798f47b4e125-image.png

Unphased the unit looks like this...
526c70c6-d966-420b-a1a7-69242391157f-image.png

That looks really epic 👍

A work of art is never finished, merely abandoned

@jip At once point we had a "phase" drone, similar in concept to the shield drone.... We abandoned this idea as it was too easy for the Phasing to be applied to a unit and become very unbalancing. (Especially if applied to an experimental) Thus we limited the Phasing to a select number of units that were non-offensive or had a non-offensive mode(s) such as the Flying Walker.

Uploaded V5 with the core UEF, Aeon and Seraphim units.
822898c5-3a53-4c1c-b7be-1e3159892a62-image.png

Side note: The mod manager leaves a lot to be desired. Having a hard time navigating things. Would like to clean out the old versions but I'm afraid to touch anything.

Resin

Forgot to add that I've updated the look of the unit phasing as it was too cloudy. It's much nicer now but I will continue to play with it till its clear but with signifigant details. I.E. Kinda of ghostly in appearance.

Do you have a Github repository for what you're converting/building here? 🙂

A work of art is never finished, merely abandoned

For Github or Git in general? If it is specifically Github, you can also use Gitlab.

The reason I am asking is because if everything is local on your machine then it becomes more difficult for people to collaborate.

A work of art is never finished, merely abandoned

@jip The core files have been around for over 10 years and in that time no one picked up the project. With that I hardly see the notion of collaboration thru Github as real possibility.

Edit: After the core units are done, sure let's Github them. Until then, changing my process this far in would be more harmful than helpful.

@resin_smoker the issue with say they been around for 10 years if they didn't have a licence that allowed other to fix them nothing we could do

"The needs of the many outweigh the needs of the few" - Spock

The reason I was asking is because ideally, when you're done with your current goals, it would turn into something such as this:

Specifically the license here is what matters. It does not have to be MIT, but at least permissive enough to allow the community to (help) maintain the mod when you decide to move on again.

At the moment we have some struggles with various well known mod packs - we can't make certain game changes because it would break them. And as we are unable to contact the authors we can also not get the permission and/or a license to setup a Github repository and maintain them. As a result we did not make those game changes.

We're lucky that you got back here and started contributing again with your mod. We have much trouble contacting the original authors simply because all we usually have is a nickname and there's no way to verify that a person with a similar nickname is the original author. Which makes it all difficult 🙂

A work of art is never finished, merely abandoned