Random Unit Models?

Civitar

Adventurer
Joined
Mar 23, 2014
Messages
1,507
Location
Switzerland
How can I randomize the models that appear in a unit? I'll be using the Tyranid insects as WH Chaos Spawn, but Spawn are supposed to have highly varied appearances. There are enough Tyranids that with a few reskins the variety will be fixed, but Spawn are huge, so I'll have only one model per unit. So how can I randomize the model that appears every time a Spawn is placed on the map?
I think PawelS' method of randomizing city names could be a good place to start.
Thank you!
 
PawelS' SQL method changes names once to a random order when the game is first started, and never again. You need a Lua method more like this one (interestingly, also to randomize city names). You'd have to have several unbuildable dummy units, copies of your spawn unit that use different models. When a unit of the specified type is created, you'd randomly replace it with one of the dummy units.
 
I think you know by now that I'm neither a Lua expert nor an expert in Lua coding for CiV, but this seemed like a fun simple-ish exercise. Let me know if there's anything you don't follow.

I used the solution from this post to ensure that the unit is actually new, rather than being "created" after, e.g., loading a save game. The part that you need from the Lua is included below, but you'll also need to add the XML from that post to your mod. [EDIT: Actually, it's not really necessary here]

Spoiler :
Code:
-- Unit_Spawn.lua
-- Author: Nutty
-- DateCreated: 6/12/2014
--------------------------------------------------------------
function Unit_Spawn(playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible)
    local player = Players[playerID]
    local unit = player:GetUnitByID(unitID)
    if(player == nil or
        unit == nil or
	unit:IsDead()) then
        return
    end

--------------
-- Choose random Spawn unit
--------------
    if player:GetCivilizationType() == GameInfoTypes["CIVILIZATION_CHAOS"] then
        local unit = Players[playerID]:GetUnitByID(unitID)
        if unit:GetUnitType() == GameInfoTypes["UNIT_SPAWN"] then
            local spawn = { "UNIT_SPAWN_V1", "UNIT_SPAWN_V2", "UNIT_SPAWN_V3", "UNIT_SPAWN_V4" }
            local unitRnd = player:InitUnit(GameInfo.Units[spawn[math.random(#spawn)]].ID, unit:GetX(), unit:GetY())
            unitRnd:Convert(unit)
        end
    end
end
Events.SerialEventUnitCreated.Add(Unit_Spawn)

Obviously, you'll need to change CIVILIZATION_CHAOS and UNIT_SPAWN* to whatever you're using in your mod. You can add however many units that you want it to pick between to the array on line 25. If it's going to be a lot, you might want to create a database table.

Like I said, you'll need to make duplicates of your Spawn unit for each model you want to use. You can use something like the following SQL, replacing the V2 and the .fxsxml model as appropriate for each one (note that the cost is set to -1 so it [the duplicates] won't be buildable):
Spoiler :
Code:
INSERT INTO ArtDefine_UnitInfos (Type,DamageStates,Formation)
	SELECT	'ART_DEF_UNIT_SPAWN_V2', DamageStates, Formation
	FROM ArtDefine_UnitInfos WHERE Type = 'ART_DEF_UNIT_SPAWN';
INSERT INTO ArtDefine_UnitInfoMemberInfos (UnitInfoType,UnitMemberInfoType,NumMembers)
	SELECT	'ART_DEF_UNIT_SPAWN_V2', 'ART_DEF_UNIT_MEMBER_SPAWN_V2', NumMembers
	FROM ArtDefine_UnitInfoMemberInfos WHERE UnitInfoType = 'ART_DEF_UNIT_SPAWN';
INSERT INTO ArtDefine_UnitMemberCombats (UnitMemberType, EnableActions, DisableActions, MoveRadius, ShortMoveRadius, ChargeRadius, AttackRadius, RangedAttackRadius, MoveRate, ShortMoveRate, TurnRateMin, TurnRateMax, TurnFacingRateMin, TurnFacingRateMax, RollRateMin, RollRateMax, PitchRateMin, PitchRateMax, LOSRadiusScale, TargetRadius, TargetHeight, HasShortRangedAttack, HasLongRangedAttack, HasLeftRightAttack, HasStationaryMelee, HasStationaryRangedAttack, HasRefaceAfterCombat, ReformBeforeCombat, HasIndependentWeaponFacing, HasOpponentTracking, HasCollisionAttack, AttackAltitude, AltitudeDecelerationDistance, OnlyTurnInMovementActions, RushAttackFormation)
	SELECT	'ART_DEF_UNIT_MEMBER_SPAWN_V2', EnableActions, DisableActions, MoveRadius, ShortMoveRadius, ChargeRadius, AttackRadius, RangedAttackRadius, MoveRate, ShortMoveRate, TurnRateMin, TurnRateMax, TurnFacingRateMin, TurnFacingRateMax, RollRateMin, RollRateMax, PitchRateMin, PitchRateMax, LOSRadiusScale, TargetRadius, TargetHeight, HasShortRangedAttack, HasLongRangedAttack, HasLeftRightAttack, HasStationaryMelee, HasStationaryRangedAttack, HasRefaceAfterCombat, ReformBeforeCombat, HasIndependentWeaponFacing, HasOpponentTracking, HasCollisionAttack, AttackAltitude, AltitudeDecelerationDistance, OnlyTurnInMovementActions, RushAttackFormation
	FROM ArtDefine_UnitMemberCombats WHERE UnitMemberType = 'ART_DEF_UNIT_MEMBER_SPAWN';
INSERT INTO ArtDefine_UnitMemberCombatWeapons (UnitMemberType, "Index", SubIndex, ID, VisKillStrengthMin, VisKillStrengthMax, ProjectileSpeed, ProjectileTurnRateMin, ProjectileTurnRateMax, HitEffect, HitEffectScale, HitRadius, ProjectileChildEffectScale, AreaDamageDelay, ContinuousFire, WaitForEffectCompletion, TargetGround, IsDropped, WeaponTypeTag, WeaponTypeSoundOverrideTag)
	SELECT  'ART_DEF_UNIT_MEMBER_SPAWN_V2', "Index", SubIndex, ID, VisKillStrengthMin, VisKillStrengthMax, ProjectileSpeed, ProjectileTurnRateMin, ProjectileTurnRateMax, HitEffect, HitEffectScale, HitRadius, ProjectileChildEffectScale, AreaDamageDelay, ContinuousFire, WaitForEffectCompletion, TargetGround, IsDropped, WeaponTypeTag, WeaponTypeSoundOverrideTag
	FROM ArtDefine_UnitMemberCombatWeapons WHERE UnitMemberType = 'ART_DEF_UNIT_MEMBER_SPAWN';
INSERT INTO ArtDefine_UnitMemberInfos (Type, Scale, ZOffset, Domain, Model, MaterialTypeTag, MaterialTypeSoundOverrideTag)
	SELECT	'ART_DEF_UNIT_MEMBER_SPAWN_V2', Scale, ZOffset, Domain, 'spawn_v2.fxsxml', MaterialTypeTag, MaterialTypeSoundOverrideTag
	FROM ArtDefine_UnitMemberInfos WHERE Type = 'ART_DEF_UNIT_MEMBER_SPAWN';
INSERT INTO ArtDefine_StrategicView (StrategicViewType, TileType, Asset )
	SELECT	'ART_DEF_UNIT_SPAWN_V2', TileType, Asset
	FROM ArtDefine_StrategicView WHERE StrategicViewType = 'ART_DEF_UNIT_SPAWN';
INSERT INTO Units (
		Type,				Description,	Civilopedia, Strategy, Help, Requirements,	Combat,	RangedCombat, Cost,	Moves, Immobile, Range, BaseSightRange,
		Class,				Special, Capture, CombatClass, Domain, CivilianAttackPriority, DefaultUnitAI, Food, NoBadGoodies, RivalTerritory, MilitarySupport, MilitaryProduction, Pillage, Found, FoundAbroad, CultureBombRadius, GoldenAgeTurns, IgnoreBuildingDefense, PrereqResources, Mechanized, Suicide, CaptureWhileEmbarked, PrereqTech, ObsoleteTech, GoodyHutUpgradeUnitClass, HurryCostModifier, AdvancedStartCost, MinAreaSize, AirUnitCap, NukeDamageLevel, WorkRate, NumFreeTechs, RushBuilding, BaseHurry, HurryMultiplier, BaseGold, NumGoldPerEra, SpreadReligion, CombatLimit, RangeAttackOnlyInDomain, RangeAttackIgnoreLOS, RangedCombatLimit, XPValueAttack, XPValueDefense, SpecialCargo, DomainCargo, Conscription, ExtraMaintenanceCost, NoMaintenance, Unhappiness,
		UnitArtInfo,				UnitArtInfoCulturalVariation, UnitArtInfoEraVariation, ProjectPrereq, SpaceshipProject, LeaderPromotion, LeaderExperience, DontShowYields, ShowInPedia,	MoveRate, UnitFlagIconOffset, PortraitIndex, IconAtlas, UnitFlagAtlas)
SELECT	'UNIT_SPAWN_V2',	Description,	Civilopedia, Strategy, Help, Requirements,	Combat,	RangedCombat, -1,	Moves, Immobile, Range, BaseSightRange,
		'UNITCLASS_SPAWN',	Special, Capture, CombatClass, Domain, CivilianAttackPriority, DefaultUnitAI, Food, NoBadGoodies, RivalTerritory, MilitarySupport, MilitaryProduction, Pillage, Found, FoundAbroad, CultureBombRadius, GoldenAgeTurns, IgnoreBuildingDefense, PrereqResources, Mechanized, Suicide, CaptureWhileEmbarked, PrereqTech, ObsoleteTech, GoodyHutUpgradeUnitClass, HurryCostModifier, AdvancedStartCost, MinAreaSize, AirUnitCap, NukeDamageLevel, WorkRate, NumFreeTechs, RushBuilding, BaseHurry, HurryMultiplier, BaseGold, NumGoldPerEra, SpreadReligion, CombatLimit, RangeAttackOnlyInDomain, RangeAttackIgnoreLOS, RangedCombatLimit, XPValueAttack, XPValueDefense, SpecialCargo, DomainCargo, Conscription, ExtraMaintenanceCost, NoMaintenance, Unhappiness,
		'ART_DEF_UNIT_SPAWN_V2',	UnitArtInfoCulturalVariation, UnitArtInfoEraVariation, ProjectPrereq, SpaceshipProject, LeaderPromotion, LeaderExperience, DontShowYields, 0,			MoveRate, UnitFlagIconOffset, PortraitIndex, IconAtlas, UnitFlagAtlas
	FROM Units WHERE Type = 'UNIT_SPAWN';
INSERT INTO Unit_AITypes (UnitType, UnitAIType)
	SELECT 'UNIT_SPAWN_V2', UnitAIType
	FROM Unit_AITypes WHERE UnitType = 'UNIT_SPAWN';
INSERT INTO Unit_ClassUpgrades (UnitType, UnitClassType)
	SELECT 'UNIT_SPAWN_V2', UnitClassType
	FROM Unit_ClassUpgrades WHERE UnitType = 'UNIT_SPAWN';
INSERT INTO Unit_Flavors (UnitType, FlavorType, Flavor)
	SELECT 'UNIT_SPAWN_V2', FlavorType, Flavor
	FROM Unit_Flavors WHERE UnitType = 'UNIT_SPAWN';
INSERT INTO Unit_FreePromotions (UnitType, PromotionType)
	SELECT 'UNIT_SPAWN_V2', PromotionType
	FROM Unit_FreePromotions WHERE UnitType = 'UNIT_SPAWN';
INSERT INTO Unit_ResourceQuantityRequirements (UnitType, ResourceType, Cost)
	SELECT 'UNIT_SPAWN_V2', ResourceType, Cost
	FROM Unit_ResourceQuantityRequirements WHERE UnitType = 'UNIT_SPAWN';
 
One thing, I do something similar with city state units to give uniques to them. I'm not certain, but sometimes these units become gifted units from militaristic city states. Watch and make certain that the Chaos Spawn are not becoming city state gifted units. I'm not certain of the rules governing such gifting... but it does occur in my modded game. Maybe -1 cost to the replacement units will eliminate this happening.
 
will whoward's point be valid with these units so far as setting cost to -1 goes?

It's the cost of -1 that's doing it (which will also cripple the upgrade cost), here how I stop one of my special units being built while giving them a standard cost

Code:
local iUnitRanger = GameInfoTypes.UNIT_RANGER

GameEvents.PlayerCanTrain.Add(function(iPlayer, iUnit) 
  if (iUnit == iUnitRanger) then
	return false;
  end

  return true
end)

I was having trouble getting a special unit to show in the civilopedia. (that's what his 'It's the cost of -1 that's doing it' comment was referring to.) I didn't want to hide the unit from the civilopedia. I just wanted to lock players from being able to train the unit in the normal ways. The Thread was here. Not a very long thread, only included for context, and wrt to unit upgrading. Thought it might be apropo. Or not.
 
@Craig_Sutter: I'm not sure offhand either, but that's a good heads-up. [Apparently, it would become an issue only where the civ is not a player in the current game.]

@LeeS: I assumed that Civitar only wants one Spawn unit to show in the 'Pedia, and I assumed that the original would have a cost associated with it. Under that scenario, it should be fine, but if not, then that's another good heads-up.
 
The cost issue will apply if the variants can upgrade.

Build Unit A (hammers 50), upon completion it has a 50/50 chance of being replaced with visual variant Unit A2 (hammers -1 so as not to be buildable). Both can upgrade to unit B (hammers 100). Only Unit A will appear in the pedia

The cost of upgrading A to B will be (100 - 50) * upgrade_multiplier (depends on game speed, but let's call it 3), ie 150 gold

The cost of upgrading A2 to B will be (100 - -1) * 3 = 300 (not 303, due to the rounding used)
 
So hang on...I make a bunch of Spawn units with identical stats except for UnitArtInfo, and set all of them to ShowInPedia=False and Cost=-1 except for the first one? Spawn, however, are unbuildable anyway - they have to be...spawned by magic.
Thanks for the help, all of you. A slightly noobish question - how do I add Lua to a mod? I've never actually needed to before:blush:.
 
So hang on...I make a bunch of Spawn units with identical stats except for UnitArtInfo, and set all of them to ShowInPedia=False and Cost=-1 except for the first one?
Yes. The SQL I provided will make one duplicate unit by copying all the attributes of a base unit.

Spawn, however, are unbuildable anyway - they have to be...spawned by magic.
Then you'll need to add something like LeeS' post for the base Spawn unit.

Oh, by the way, I revised the Lua code in #5 above to remove the initialization check, which isn't necessary here.

how do I add Lua to a mod? I've never actually needed to before:blush:.
InGameUIAddin (see whoward69's File Attributes thread, post #3). You won't see your Lua file in the dropdown; you'll need to type it in manually.
 
InGameUIAddin (see whoward69's File Attributes thread, post #3). You won't see your Lua file in the dropdown; you'll need to type it in manually.

I always do the 'File' 1st because that allows selection of the lua file from a drop-down. Then I select UIAddin for the top field. Then I add the middle two. I saw this trick somewhere else on the forum, or perhaps it was farther down in whoward's tutorial. It's always worked for me.
 
Then you'll need to add something like LeeS' post for the base Spawn unit.

Just to be clear in case it got lost in translation. That 'no train' function was a bit of whoward's magic he provided me.
 
You mentioned the problem with making promotions invisible in another thread, and I realized you don't need Machiavelli24's solution after all. Rather than making the base unit one of the random possibilities, you can use that one as the placeholder. Regardless of how the game is "creating" one (including embarking and loading a game), we'll know it hasn't been randomized yet if and only if it's the base unit.

I changed the code in post #5.
 
@Nutty: So let me get this straight. I should use the updated code in post #5 to randomize the units from a list that will not include the base unit, and the base unit will never appear if it works? So, I should make the base unit look like a tank or something?
 
That's correct. It doesn't matter what the base unit looks like, but you can use a tank if you want to make sure it's working properly.

In fact, you could make the base unit be a totally different unit (it doesn't even have to be the same class), to work around the militaristic city states issue that Craig_Sutter mentions in post #6. Apparently, it becomes an issue only if a user has your mod active but the civ in question is not a player in the current game.

The only problem is that the unique unit--I assume you want it to be considered a UU for that civ--won't be displayed correctly in the loading screen (if you're playing as the civ) and the Civilopedia. A possible workaround for the loading screen is to make an unbuildable unique building using the icon for your unique unit... [and don't actually make the unit unique to this civ in Civilization_UnitClassOverrides.]

EDIT: Oh, wait, these are magically spawned, so you probably don't need to worry about the UU stuff, right?
 
Back
Top Bottom