Now comes the part I hate the most: the Decorate code.
To simplify the process a bit, here I have provided a little infographic to help you understand how ECwolf/LZwolf understand the coordinates supplied by the most important command we will be using (A_SpawnItemEx):

Now, the Decorate code itself is fairly straightforward, but with some idiosyncrasies specific to EC/LZ that ZDoom users may find strange which I will explain as they come up.
////////////////////////////////////////////////////////////////////////////////////////
// Sloped light fixtures (E1M1) //
////////////////////////////////////////////////////////////////////////////////////////
Actor QSlopedLightCeil1a : QBillboard
{
States
{
Spawn:
DQ00 Q -1
Stop
}
}
Actor QSlopedLightCeil1b : QBillboard
{
States
{
Spawn:
DQ00 R -1
Stop
}
}
Actor QSlopedLightCeil1c : QBillboard
{
States
{
Spawn:
DQ00 S -1
Stop
}
}
Actor QSlopedLightCeilld : QBillboard
{
States
{
Spawn:
DQ00 T -1
Stop
}
}
You can see here there is nothing particularly special about these objects, except that in this case they descend from a class called “QBillboard”. This is just a class that has a bunch of basic properties I use for all billboard objects in my project, which in this case sets “+BILLBOARD”, “-SOLID”, and if you care, they have radii of 16.
The fun really begins when we get to the spawner objects:
Actor QSlopedLightCeil1_least
{
States
{
Spawn:
TNT1 A 0
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1a",0,32,0,0,0,0,90)
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1e",0,30,0,0,0,0,90)
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1b",0,-32,0,0,0,0,90)
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1f",0,-30,0,0,0,0,90)
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1c",64,32,0,0,0,0,90)
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1g",64,30,0,0,0,0,90)
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1d",64,-32,0,0,0,0,90)
TNT1 A 0 A_SpawnItemEx("QSlopedLightCeil1h",64,-30,0,0,0,0,90)
Stop
}
}
What a mess, eh? There’s no easy way around this – you’re just going to have to figure the spawn coordinates out on your own, but I hope the infographic above will be of some help to you when doing this.
I’ll try to simplify a bit by explaining the SpawnItemEx syntax, as I use it:
A_SpawnItemEx(x offset, y offset, 0, 0, 0, 0, angle)
…Where the x and y offsets are taken from the center of the tile, which is considered 0,0. The sprite “TNT1” is used here because not only is it guaranteed to be an empty sprite, but the engine is aware of this specific sprite name and will completely skip any rendering on it, so it’s actually faster than supplying your own “BLNKA0” sprite or whatever. Cool!
You may have also noticed there is an empty zero-length state at the beginning of the spawner’s states. This is intentional; this avoids a strange behavior in EC/LZ where A_SpawnItemEx fails to work in certain circumstances which are satisfied if that state wasn’t there. Don’t worry about it too much – just remember to include an extra zero-length state at the beginning of your spawn process.