Entity modifiers

I made this tool that switches one screen to another prop/entity.
Now I’m trying to make it behave nice with (Advanced) Duplicator.
[lua]
local function switchscreen(screen, ent)
screen.GPUEntity = ent
umsg.Start(“wire_gpulib_setent”)
umsg.Short(screen:EntIndex())
umsg.Short(ent:EntIndex())
umsg.End()

	duplicator.StoreEntityModifier(screen, "wire_gpulib_switcher", { ent })
end

duplicator.RegisterEntityModifier("wire_gpulib_switcher", function(ply, screen, data)
	print(ply, screen, data)
	PrintTable(data)
	local ent = data[1]
	switchscreen(screen, ent)
end)

[/lua]

Now the problem is that the “PrintTable(data)” line always prints the old entity, not the newly created one.

Is there a solution that gives me the newly created entity somehow?
Support for the regular duplicator is kind of optional, but would still be nice :slight_smile:

Support for the Regular duplicator comes free with the advanced duplicator in my experience.
To get the newly created entity the only method I’ve seen that works is through the PostEntityPaste entity hook as it gives the table of newly created entities where the key is the entity ID of the old entity. You’d end up having to put the following in the screen’s PostEntityPaste hook:
[lua]function ENT:PostEntityPaste(ply, ent, createdEnts)
– so we don’t break wire duping
self.BaseClass.PostEntityPaste(self, ply, ent, createdEnts)
switchscreen(self, createdEnts[ ent.EntMods.wire_gpulib_switcher[1] ])
end[/lua]
Also you’d have to change this
[lua]-- old function
– duplicator.StoreEntityModifier(screen, “wire_gpulib_switcher”, { ent })
– new function
duplicator.StoreEntityModifier(screen, “wire_gpulib_switcher”, { ent:EntIndex() })[/lua]

[editline]12:00AM[/editline]

I’ve been trying to understand the complexities of the duplication system myself.
RegisterEntityClass allows you to define a function for creating the specific entity class, it fails to work if you’re defining for a base class as all subclasses will have to redefine it.
The EntityModifiers are handy for if you want to alter an entity but can’t alter its definition. A useful example is the Mass STool, which stores a change in mass which gets applied when it’s duped. This is a generic method of duplicator alteration, which unfortunately still has the drawback of not being aware of other entities that have been pasted.
The PreEntityCopy hook is called before it copies the entity, this can be useful for storing your entity modifiers if there wasn’t a better moment to do it beforehand. PostEntityPaste gets passed the CreatedEntities list, which contains references to the new entities with indexes of the entity index of the original entity. The PostEntityPaste method does have the shortcomings of it not knowing about the created Constraints, which could be fixed by calling PostEntityPaste after the constraints have been created.
In short, there are problems with the system which ought to be addressed.

Does anyone else find the new submission form keeps stripping out their lua tags?

[editline]12:36AM[/editline]

Redefining the following would allow you to have the CreatedEnts and CreatedConstraints as parameters for the entity modifier and PostEntityPaste.
[lua]function duplicator.Paste( Player, EntityList, ConstraintList )

local CreatedEntities = {}

//
// Create the Entities
//
for k, v in pairs( EntityList ) do

    CreatedEntities[ k ] = duplicator.CreateEntityFromTable( Player, v )
    
    if ( CreatedEntities[ k ] ) then
    
        CreatedEntities[ k ].BoneMods = table.Copy( v.BoneMods )
        CreatedEntities[ k ].EntityMods = table.Copy( v.EntityMods )
        CreatedEntities[ k ].PhysicsObjects = table.Copy( v.PhysicsObjects )
        
    else
    
        CreatedEntities[ k ] = nil
    
    end
    
end

local CreatedConstraints = {}

//
// Create constraints
//
for k, Constraint in pairs( ConstraintList ) do

    local Entity = duplicator.CreateConstraintFromTable( Constraint, CreatedEntities )
    
    if ( Entity && Entity:IsValid() ) then
        table.insert( CreatedConstraints, Entity )
    end

end


//
// Apply modifiers to the created entities
//
for EntID, Ent in pairs( CreatedEntities ) do    

    ApplyEntityModifiers ( Player, Ent, CreatedEntities, CreatedConstraints )
    ApplyBoneModifiers ( Player, Ent, CreatedEntities, CreatedConstraints )

    if ( Ent.PostEntityPaste ) then
        Ent:PostEntityPaste( Player, Ent, CreatedEntities, CreatedConstraints )
    end

end

return CreatedEntities, CreatedConstraints

end

/---------------------------------------------------------
Applies entity modifiers
---------------------------------------------------------
/
function duplicator.ApplyEntityModifiers( Player, Ent, CreatedEnts, CreatedConstraints )

if ( !Ent ) then return end
if ( !Ent.EntityMods ) then return end

for Type, ModFunction in pairs( EntityModifiers ) do

    if ( Ent.EntityMods[ Type ] ) then

        ModFunction( Player, Ent, Ent.EntityMods[ Type ], CreatedEnts, CreatedConstraints )
        
    end
end

end[/lua]

oh damnit, I just found out that advdupe applies entity modifiers immediately after creating the entity, as opposed to after all entities…

hmm, I could register a constraint…
the main disadvantage would be that it wastes an edict slot for no good reason

[editline]07:04AM[/editline]

my solution:
[lua]-- Calls func once (Advanced) Duplicator has finished spawning the entity that previously had the entity id entid.
– Usage: WireLib.PostDupe(entid, function(ent) … end)
function WireLib.PostDupe(entid, func)
local CreatedEntities

local paste_functions = {
	[duplicator.Paste] = true,
	[AdvDupe.Paste] = true,
	[AdvDupe.OverTimePasteProcess] = true,
}

-- Go through the call stack to find someone who has a CreatedEntities table for us.
local i,info = 1,debug.getinfo(1)
while info do
	if paste_functions[info.func] then
		for j = 1,20 do
			local name, value = debug.getlocal(i, j)
			if name == "CreatedEntities" then
				CreatedEntities = value
				break
			end
		end
		break
	end
	i = i+1
	info = debug.getinfo(i)
end

-- Nothing found? Too bad...
if not CreatedEntities then return end

-- Wait until the selected entity has been spawned...
local unique = {}
timer.Create(unique, 1, 240, function(CreatedEntities, entid, unique, func)
	local ent = CreatedEntities[entid]
	if ent then
		timer.Remove(unique)
		
		-- and call the callback
		func(ent)
	end
end, CreatedEntities, entid, unique, func)

end[/lua]

works with both duplicator and advanced duplicator and will be in wiremod r2001 :slight_smile:
can be used out of an entity modifier handler and should also be usable from an entity creation hook, like duplicator.RegisterEntityClass