Hwmod Wiki


As of May 2020 the Homeworld Remastered section of this wiki has been depreciated. The content is currently being consolidated into the new Homeworld Remastered Karos Graveyard.

June 7th, 2016 Modding Changes[]


  • New codeside tool for setting shader properties on ships and subsystems, and changing them via LUA (possible uses include call signs, kill tallies, etc)
  • Shader values can be connected to navlights, allowing the ability to alter navlight visibility via LUA

You can also use Material PARAM[] nodes in the DAE to change the shader values from outside - something I see almost nobody using... And that can be very, very powerful in this context. I think that is used in a few example assets?

Look at Asteroid_3 for an example. You need a HOLD_PARAMS node under your ROOT_LOD[0] node. It acts as the parent for the parameter tweaks.

MAT[Asteroid3]_PARAM[fadeWindow]_Type[RGBA]_Data[.25,0,.75,0] This sets the fadeWindow (it's linked to the asteroid resourcing progress effect) uniform of the material Asteroid3 to the new values. (I don't think it's actually a color like RGBA, it's just 4 float values (Vector4)).

I don't know about other available types.

Any property exposed in the .surf file for your material can be overloaded at the material level from within the DAE. You can make glows darker, affect the spec curve, etc. Anything the .surf has made 'public' - in effect. And if you find one you want public, but isn't - do it local to your mod, that's very easy too.


  • HODs will now automatically share textures with the same name to reduce memory load.
  • Turrets are now nearly all subsystems, to improve performance/reduce memory load.
  • Direction and Rest joints are now optional and can be left out to improve performance.
  • Effects marker names must be valid - improved performance.
  • LUA file can now be used to manually add new hardpoints, etc, to stock HODs.


  • Scuttling a ship results in a push effect and blast damage to nearby ships.


  • Repair actions can heal ships of different types in different ways.
  • Stock ship textures and details improved.
  • Stock FX cleaned and improved.


  • SobGroup_LeaveStrikeGroup

HWR .ship file additions

  • setTacticsMults(NewShipType, "MAXSPEED", 0.80, 1.10, 1.0)
  • setTacticsMults(NewShipType, "ENGINEACCEL", 1.20, 0.80, 1.0)
  • setTacticsMults(NewShipType, "ENGINEBRAKE", 1.0, 1.0, 1.0)
  • setTacticsMults(NewShipType, "THRUSTER", 1.0, 1.0, 1.0)
  • setTacticsMults(NewShipType, "THRUSTERACCEL", 1.20, 0.80, 1.0)
  • setTacticsMults(NewShipType, "THRUSTERBRAKE", 1.0, 1.0, 1.0)
  • setTacticsMults(NewShipType, "ROTATION", 1.0, 1.10, 1.0)
  • setTacticsMults(NewShipType, "ROTATIONACCEL", 1.0, 1.10, 1.0)
  • setTacticsMults(NewShipType, "ROTATIONBRAKE", 1.0, 1.0, 1.0)
  • setTacticsMults(NewShipType, "WEAPONACCURACY", 1.0, 1.0, 1.0)
  • setTacticsMults(NewShipType, "WEAPONDAMAGE", 1.20, 1.0, 1.0)
  • setTacticsMults(NewShipType, "BULLETSPEED", 1.15, 1.0, 1.0)
  • setTacticsMults(NewShipType, "DAMAGEAPPLIED", 1.10, 0.90, 1.0)
  • setTacticsMults(NewShipType, "FIRERATE", 1.0, 1.25, 1.0)
  • setSpeedvsAccuracyApplied(NewShipType,100.0,5.0,120,1.0,250,1.0,388,0.98,535,0.70)
  • NewShipType.minFalloffDamageDist=15
  • NewShipType.maxFalloffDamageDist=15*3
  • NewShipType.maxFalloffScuttleDamageDist=15*6
  • NewShipType.explosiveScuttleDamageOnDeath=15
  • NewShipType.maxFalloffForce=15*10
  • NewShipType.explosiveDamageOnDeath=3.4
  • NewShipType.radiusDamageEvadeMod=1.1
  • NewShipType.hideNormalAttackUICooldown=1
  • NewShipType.agileFlight=1
  • NewShipType.SquadronSize=getShipNum(NewShipType, "SquadronSize", 1)
  • NewShipType.buildBatch=getShipNum(NewShipType, "buildBatch", 5)
  • NewShipType.batchFormation="Batch_delta"
  • NewShipType.noCombatGrouping=1

HWR .wepn file additions

  • setMissProperties(NewWeaponType, 0.02, 0.04, 0.20, 0.30, 0.50, 0.50);
  • setSpeedvsAccuracyAgainst(NewWeaponType, 1, 50.0, 1.5, 75, 1.0, 300, 1.0, 487, 0.90, 535, 0.60);
  • setLifetimeMult(NewWeaponType, 1.75);
  • setDamageFalloff(NewWeaponType, 0.25/2000.0, 0.1);
  • setAccuracyFalloff(NewWeaponType, 0.25/2000.0, 0.1);
  • setBallistics(NewWeaponType,0,0.0);
  • setMissileKiller(NewWeaponType,1);

August 13, 2015 Modding Changes[]

As of August 13, 2015, Gearbox began changing the way that Homeworld Remastered reads files/directories/etc in order to make for a more powerful and flexible modding experience. Changes are ongoing, the biggest change so far being that many AI, research, build, and related logic files moved to their own race-specific folder. Review the below linked threads for full details.

Summary of Important Data Format Changes

Summary of Important Script System Changes

Filtering properties to control what content is viewable in a mod.

Changes to map files to allow content filtering.

Race Construction Questions

Review of DoScanPath functionality.


Reference guides for HWR modding post August 13, 2015 patch.




Data Directory overview[]

SpawnSalvageOnDeath() has a new parameter. I forget what it is or what it does, but it's crash-worthy.

BUILDANDRESEARCH folder is dead and gone. SHADERS folder is full of brand new files.

DATA > SCRIPTS contains a new sub-folder RACES

RACES contains sub-folders with each race name, and a matching racename.lua file


PROPS contains default.lua which is where many race properties are set

RACENAME > SCRIPTS contains RESEARCH sub-folder and ai_build.lua, ai_special.lua, ai_subsystems.lua, ai_upgrades.lua, crate_locate.lua, crate_ships.lua, def_build.lua, and def_research.lua

DATA > SCRIPTS contains a new sub-folder RULES

RULES contains sub-folders DEATHMATCH, SINGLEPLAYER, deathmatch.lua and singleplayer.lua DEATHMATCH folder contains UNITCAPS sub-folder UNITCAPS folder contains huge.lua, large.lua, normal.lua, and small.lua

DATA > AI contains sub-folder DEFAULT

DEFAULT contains classdef.lua, cpubuild.lua, cpubuildsubsystem.lua, cpumilitary.lua, cpuresearch.lua, cpuresource.lua, default.lua, hiigaran_upgrades.lua, hw1cpuplayerlayer.lua, vaygr_upgrades.lua

Texture Reference[]

Note: These are the most common textures used for ship and thruster shaders. Other shaders use additional custom maps. Refer to SHADERS.MAP file for full listing.

DIFF = standard diffuse map. No alpha channel.
DIFX = diffuse, thruster shader "off" status
REFL = RGB channels (grayscale) black = non-reflective, white = fully reflective
SPEC = RGB channels (grayscale) black = not shiny, white = very shiny
GLOW = RGB channels (grayscale) black = no self-illumination, white = fully self-illuminated
PAIN = alpha channel (grayscale) black = metal, white = paint (?)
STRP = alpha channel (grayscale) black = no stripe color, white = stripe color
TEAM = alpha channel (grayscale) black = no team color, white = team color
GLOX = self-illumination, thruster shader "off" status
STRX = stripe color, thruster shader "off" status
TEAX = team color, thruster shader "off" status
SPEX = specular, thruster shader "off" status
REFX = glossiness, thruster shader "off" status
NORM = Standard normal map. No alpha.

Output files:

See SHADERS.MAP file for listing of all texture output formats.

(Posted by HerbyGuitar on the Relic Forums 23/03/15)

HWR uses a psuedo PBR rendering system.

HWRM Shader for Blender Cycles


Shader Thread on GBX forums

important shader related files:

  • SHADERS.MAP - used by HODOR during HOD creation. defines what shaders as declared in the DAE use what textures, and how they combine in the final HOD.
  • HOD_ALIAS.MANIFEST - located in data > shaders, associates .surf files with shader names that are referenced in 3dsMax when creating the DAE (see above).
  • MOD_ALIAS.MANIFEST - same function as HOD_ALIAS.MANIFEST, for use by mod authors. Place any custom shader names here.

Note: following is a list of most commonly used default shaders. Refer to above listed files for a complete listing of stock shaders.

ship pretty much all regular ships
badge applied to polygons where the user selectable badge texture will show up
thruster allows engine on/engine off textures
matte used for many background/static map objects - does not interact with hyperspace effects

UI Reference

general FE (front end) elements are controlled in:

ui > newui > styles > hwrmstyles.lua

FE button styles are defined in: ui > newui > styles > hwrm_style > hwrmdefines.lua

to create new tactical overlay icons, add new .hod's to ui > tacticalicons > meshes, be sure to create a corresponding TI file.


Texture and Memory Considerations

from Bitvenom (http://forums.gearboxsoftware.com/t/update-and-a-bit-of-a-tease/1203614/394)

>Yes and Yes, I think. I mean, not all content is loaded, certainly. And if you can only have 2 players, well, you can only have 2 players :slightly_smiling: But make sure you take into account stuff like your backgrounds, and FX...

Our backgrounds are typically 40MB without any planets or extra stuff, up to 120MB with all of the fun stuff - a 'plain' background over 50MB is probably just an example of poor material/texture use... For example a raw Cube instead of much, much more optimal Sphere. In addition, consider the sorts of detail your background has/needs... You can use stars or even cut-out overlays to provide details on top of a MUCH lower-res base background. It is very easy to add new stars for hero items like small nebula or constellations - as opposed to pushing your background res high enough to make those sorts of things sharp.

For us, our most complicated backgrounds are in Campaign maps - we know exactly what will be in that level. For MP, often the most basic maps are best - leave the performance for other things. I've seen a fair amount of 'Cube' constructed maps - but those are insane: We use a single 4kx2k texture for our basic backgrounds - they often look amazing. As noted before, the stars and smaller details can be sharp as just extras not actually pixels in the background texture itself. A 'Cube' is going to be 2kx2k or more, per side - So that's 3x more RAM used, where often the top/bottom aren't seen. It's not only way slower to draw/render, it's wasteful - plus, the pixels for any given texture vary in screen-space size because they're smaller farther towards the edges of the cube.

For FX, the balance is similar - use low-res textures when you can for things that aren't sharp, stick to only a size that you require - bigger is NOT better.

And lastly, consider UI - here's another case where being conservative is king. That's because UI is ALWAYS loaded. Sure some of the ship-specific stuff only comes along with loaded ships (ShipIcons, etc) - but in general, super HQ UI art is burning your budget. Ship Icons are a great spot to screw up as well. Some authors insist on using big 'view' graphics - when often the on-screen size doesn't justify it. Or, they use non power-of-2 textures and choose shapes/aspects that waste RAM, often the same mistake over and over for each ship loaded - compounding the waste. A 512x256 preview icon is probably more than enough for most uses, and as a DDS ends up being tiny. You can scale it to 1024x512 easily - which is a substantial portion of even a 4k screen.

Sorry to harp - but I get really cranky when I see artists burning budget without any regard for a balance of end-result vs performance.

>The budget in this case is just raw RAM - so if your LODs have unique textures, they're making the pressure for memory as a resource worse. Enough to matter? Probably not. As for LODs - the old advice was to not bother. However, the next patch doesn't support Goblins any longer - they're not an engine feature at all. We use our first LOD as the ship+goblins, and our 2nd LOD as the ship - then lower as required. So we're using LODs a bit. Not a ton, but some.

>I want to say we're at 1.7GB (last time I checked) - with 4 races in MP. There's not a ton of RAM left after that. If you double that (8 races) - and use races that are (aggregate) 50% bigger - that's like loading 12 races (8*1.5) - where in theory you had room for... 5?

>Nah, there's TONS of room - but games have budgets. We delivered a game in our resource budget at the highest level possible. If you double the content (pixels, polygons, etc) - and not the budget, bad things will happen. The budget is largely a limit of the type of engine/exe (32bit) that we are. So Mod authors can add more, but it can come at the cost of having to use lower-res textures, etc. You can't clone the races the game shipped with and then make them '50% bigger' just because you want more pixels without having to account for that somewhere.


A ship can have 16 total lights applied to it. Generally, that should be kept to 4-5 navlights... though if they blink or aren't on at the same time, higher can be okay. This leaves space for other lights (bay lights, engines, explosions, etc).

Subsystems don't allow any lights to be cast from them in the upcoming patch.

However, also in the patch is a system to assign 'channels' to nav lights that can be affected by certain LUA commands - which will be documented a bit after the patch itself is out.

Take a look at some of our ships - I think maybe in the examples and our 'nav light styles' files - there are many ways to balance lights that actually cast light...

Bake your 'bay' surfaces and use the sob_bay shader - but have a light in the bay that casts on anything except the host ship. We do that often.

Decorative lights on the main ship take away from battle/dynamic lights on the ship, but it is much worse if they bleed on to other ships and also pull them down - consider flagging those as affecting the host ship only.

Light gathering is a radius-based operation, ships with huge radius (MS, etc) tend to gather up LOTS of lights - so really consider the scale of your lights to not end up being considered for too many ships where they just won't be visible on the surface.

Navlights 'updating' can actually be a notable percent of the per-frame update time when you have a large army. Avoid using Navlights on small ships where they're likely to have huge populations. I don't just mean 'light casters' in this context, I mean all Navlights (sprite and casters).

Thruster lights are great - but stick to frigate or larger ships to prevent those from overwhelming other lights in the scene when ships are close together.

Pouk: We're talking only a light casting NavLights here, right? Not all the possition tiny red lights? Correct - there's no effective limit on sprite Navlights - though you should still consider being conservative, their impact on large-population ships can add up quickly.

Key/Fill/Ambiet 16 Point lights (FX/engines/ships/etc) 16 Beam lights (FX) If you haven't seen more, it may just be that the gather radius on the ships is picking up lights in that radius, but not large/close enough to visibly effect the ship surface itself. The gather logic is really just basic radius overlap tests with strength weighting.

You can edit a custom ship shader to show a flat color based on the light count for a specific ship (or add a control to show only one light at a time) if you want to debug. I've done that a few times when I wasn't sure.


"px" - Pixels (scale can apply) "abs_px" - Pixels (never scaled) "scr" - Screen (Width or Height by context) "scr_min" - Smallest of Screen Width/Height "scr_max" - Largest of Screen Width/Height "scr_43" - Width or Height of value resulting in a 4:3 ratio fit of Screen "par" - Parent (Width or Height by context) "par_min" - Smallest of Parent Width/Height "par_max" - Largest of Parent Width/Height

Adding a new button with custom code https://forums.gearboxsoftware.com/t/selection-filerinclude-selection-filterexclude/1865530/8?u=sastrei

In Ben_RuCarrier.ship:

addAbility(NewShipType,"CustomCommand",1,"Missile",1,0,600, 0, 1, 20,100,"data:ship/Ben_RuCarrier/Ben_RuCarrier.lua","Start_Ben_RuCarrier","Do_Ben_RuCarrier","Finish_Ben_RuCarrier","Ben_RuCarrier",0.2,1,1,0) The format is:

addAbility(NewShipType,"CustomCommand",<bIsActive>,<sName>,<numUnknown1>,<numUnknown2>,<fCapacity>,<fEnergyCutoff>,<fEnergyCost>,<fEnergyRegen>,<fEnergyMinimum>,<sCustomCodePath>,<sFunctionStart>,<sFunctionDo>,<sFunctionFinish>,<sCustomGroup>,<fRefreshTime>,<iCmdIndex>,<?bLatent?bExplode?>) Arguments

<bIsActive>: is the ability active by default? (0 = inactive, 1 = active) <sName>: Seems like the name of the custom ability? <numUnknown1>: Unknown, usually is 1. <numUnknown2>: Unknown, usually is 0. <fCapacity>: Total energy. <fEnergyCutoff>: if the remaining energy is this much or lower, player can stop the ability manually. <fEnergyCost>: Energy cost per 0.1 seconds <fEnergyRegen>: amount of energy recharged per 0.1 seconds. <fEnergyMinimum>: minimum amount of energy required to activate the ability. <sCustomCodePath>: path to the script file. <sFunctionStart>: name of the "start" function. <sFunctionDo>: name of the "do" function. <sFunctionFinish>: name of the "finish" function <sCustomGroup>: the name of a Sobgroup which will contain the ship. <fRefreshTime>: The "do" function will be excuted every <fRefreshTime> seconds. <iCmdIndex>: The ability icon(defined in ui\newui\taskbar\tb_commandpanel.lua) <bLatent>: custom command flag to make it run as latent - If true, this custom command can run while another command is active (used to allow commands to keep running while you move ships, attack, etc). <bExplode>: custom command flag to destroy ship when depleted - When usage energy runs out, ship will explode. Note: it is unknown if the last parameter is <bLatent> or <bExplode>, in fact, if we look at Gearbox’s code:

addAbility(NewShipType,"CustomCommand",1,"Drones",1,0,1000,200,0.35,2.8,0,"data:Ship/Kus_DroneFrigate/Kus_DroneFrigate.lua","Start_DroneFrigate","Do_DroneFrigate","Finish_DroneFrigate","Kus_DroneFrigate",1.15,2,1) addAbility(NewShipType,"CustomCommand",1,"$3191",1,0,300,1,0.65,0.0,300,"data:Ship/Kus_GravWellGenerator/Kus_GravWellGenerator.lua","Start_Kus_GravWellGenerator","Do_Kus_GravWellGenerator","Finish_Kus_GravWellGenerator","Kus_GravWellGenerator",1.9,1,1,1,1) You will see that sometimes they use a single “1” after <iCmdIndex>, while sometimes they use 3 "1"s.

CamPos Script https://forums.gearboxsoftware.com/t/solved-camera-x-y-z-query/1542710/16?u=sastrei

Complete fixed code:

data:ship/hgn_staticprobe/ copied hgn_probe folder and re-named files. data:ship/hgn_staticprobe/hgn_staticprobe.ship – modified lines:

-- just in case SobGroup_SetInvulnerability fails NewShipType.maxhealth=getShipNum(NewShipType, "maxhealth", 100000) NewShipType.regentime=1 NewShipType.minRegenTime=1


NewShipType.thrusterMaxSpeed=0 NewShipType.mainEngineMaxSpeed=0 NewShipType.rotationMaxSpeed=0


-- changing these didn't actually seem to affect the sway? NewShipType.swayUpdateTime=0 NewShipType.swayOffsetRandomX=0 NewShipType.swayOffsetRandomY=0 NewShipType.swayOffsetRandomZ=0 NewShipType.swayBobbingFactor=0 NewShipType.swayRotateFactor=0


addAbility(NewShipType,"MoveCommand",0,0); data:leveldata/multiplayer/lib/camerautils.lua


function Camera_Init()

   Volume_AddSphere("Volume_Map0", {0, 0, 0}, 0.01)
   Volume_AddSphere("Volume_MapX", {AXIS_DISTANCE, 0, 0}, 0.01)
   Volume_AddSphere("Volume_MapY", {0, AXIS_DISTANCE, 0}, 0.01)
   Volume_AddSphere("Volume_MapZ", {0, 0, AXIS_DISTANCE}, 0.01)
   SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_0", "SobGroup_Map0", "Volume_Map0")
   SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_X", "SobGroup_MapX", "Volume_MapX")
   SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_Y", "SobGroup_MapY", "Volume_MapY")
   SobGroup_SpawnNewShipInSobGroup(0, "Hgn_StaticProbe", "MAP_Z", "SobGroup_MapZ", "Volume_MapZ")
   SobGroup_SetInvulnerability("SobGroup_Map0", 1)
   SobGroup_SetInvulnerability("SobGroup_MapX", 1)
   SobGroup_SetInvulnerability("SobGroup_MapY", 1)
   SobGroup_SetInvulnerability("SobGroup_MapZ", 1)
   Rule_AddInterval("UpdateCameraPos", 1)


function Camera_GetPosition()

   local a = Camera_GetDistanceToSobGroup("SobGroup_Map0")
   local b = Camera_GetDistanceToSobGroup("SobGroup_MapX")
   local c = Camera_GetDistanceToSobGroup("SobGroup_MapY")
   local d = Camera_GetDistanceToSobGroup("SobGroup_MapZ")
   local z = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - d*d) / (2*AXIS_DISTANCE);
   local y = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - c*c) / (2*AXIS_DISTANCE);
   local x = (AXIS_DISTANCE*AXIS_DISTANCE + a*a - b*b) / (2*AXIS_DISTANCE);
   return { X = x, Y = y, Z = z};


function UpdateCameraPos()

   local pos = Camera_GetPosition();
   ATI_AddString(0, format("campos: %.4f, %.4f, %.4f", pos["X"], pos["Y"], pos["Z"]))
   ATI_Display2D("Message", {0.02, 0.2, 0, 0}, 0)

end data:leveldata/multiplayer/lib/camera_ati.lua:

SCAR_ATITemplates = {

   Message = {
           stringParam = 0,
           text = {
               colour = {1, 1, 1, 1},
               dropshadow = 1,
               renderFlags = { "justifyLeft" },
               LODs = { 1, "SPSubtitleFont" }
           placement2D = {
               factorX = -0.05,
               factorY = -1,
               minATIArea = 0,
               maxATIArea = 1,
               visibility = {}

} In data:scripts/rules/deathmatch/deathmatch.lua:

Add dofilepath("data:leveldata/multiplayer/lib/camerautils.lua") after other dofilepaths. Add Camera_Init() before end of OnInit()

Run the game. See Camera Position displayed.

20160802023012_1.jpg1366×768 84.2 KB Also, ships tend to have a random “sway” attached to them, even when stationary, so this isn’t a 100% exact science… But it will get you 99.9% there, which in my book is plenty! :wink:

Miscellaneous Tips[]

  • Race prefixes must be three letters or build menus will not work correctly, apparently.
  • To turn on aitrace logging, edit the following section in ai > default > default.lua

aitrace = function(aiTraceOutput)