Just did some scanning, it looks like civs can, in fact, change their own ethics.
entity.entity_raw.ethic is information from the world's RAWs, while
entity.resources.ethic is what the civ actually uses.
If you run the following script:
-- Find oddities across civ ethics
ethic_fields = {"KILL_ENTITY_MEMBER","KILL_NEUTRAL","KILL_ENEMY","KILL_ANIMAL","KILL_PLANT","TORTURE_AS_EXAMPLE","TORTURE_FOR_INFORMATION","TORTURE_FOR_FUN","TORTURE_ANIMALS","TREASON","OATH_BREAKING","LYING","VANDALISM","TRESPASSING","THEFT","ASSAULT","SLAVERY","EAT_SAPIENT_OTHER","EAT_SAPIENT_KILL","MAKE_TROPHY_SAME_RACE","MAKE_TROPHY_SAPIENT","MAKE_TROPHY_ANIMAL"}
ethic_enum = {[0]="NOT_APPLICABLE",[1]="ACCEPTABLE",[2]="PERSONAL_MATTER",[3]="JUSTIFIED_IF_NO_REPERCUSSIONS",[4]="JUSTIFIED_IF_GOOD_REASON",[5]="JUSTIFIED_IF_EXTREME_REASON",[6]="JUSTIFIED_IF_SELF_DEFENSE",[7]="ONLY_IF_SANCTIONED",[8]="MISGUIDED",[9]="SHUN",[10]="APPALLING",[11]="PUNISH_REPRIMAND",[12]="PUNISH_SERIOUS",[13]="PUNISH_EXILE",[14]="PUNISH_CAPITAL",[15]="UNTHINKABLE",[16]="REQUIRED"}
function getEthicString (ethics)
local result = ""
for _,name in ipairs(ethic_fields) do
result = result .. string.format("%02i",ethics[name])
end
return result
end
-- Iterate across all civs
for civ_id,entity in ipairs(df.global.world.entities.all) do
local raw_ethic = getEthicString(entity.entity_raw.ethic)
local sav_ethic = getEthicString(entity.resources.ethic)
-- Throw a message if the ethics strings are different from each other
if raw_ethic ~= sav_ethic then
print("civ_id="..civ_id,dfhack.TranslateName(entity.name),"Ethics don't match!")
print(string.format(" [ENTITY:%s]",entity.entity_raw.code))
for _,field in ipairs(ethic_fields) do
if entity.entity_raw.ethic[field] ~= entity.resources.ethic[field] then
print(string.format(" [ETHIC:%s:%s]\n RAWs say %s",field,ethic_enum[entity.resources.ethic[field]],ethic_enum[entity.entity_raw.ethic[field]]))
end
end
end
end
You can get nice-looking output like the following:
[DFHack]# index-ethics
civ_id=326 Ongetodur Ethics don't match!
[ENTITY:MOUNTAIN]
[ETHIC:KILL_NEUTRAL:REQUIRED]
RAWs say ONLY_IF_SANCTIONED
[ETHIC:KILL_ENEMY:REQUIRED]
RAWs say ACCEPTABLE
civ_id=393 Kimenemmun Ethics don't match!
[ENTITY:PLAINS]
[ETHIC:KILL_NEUTRAL:REQUIRED]
RAWs say JUSTIFIED_IF_NO_REPERCUSSIONS
[ETHIC:KILL_ENEMY:REQUIRED]
RAWs say ACCEPTABLE
civ_id=402 Gusilgadan Ethics don't match!
[ENTITY:MOUNTAIN]
[ETHIC:KILL_NEUTRAL:REQUIRED]
RAWs say ONLY_IF_SANCTIONED
[ETHIC:KILL_ENEMY:REQUIRED]
RAWs say ACCEPTABLE
civ_id=413 Kalsiibu Ethics don't match!
[ENTITY:PLAINS]
[ETHIC:KILL_NEUTRAL:REQUIRED]
RAWs say JUSTIFIED_IF_NO_REPERCUSSIONS
[ETHIC:KILL_ENEMY:REQUIRED]
RAWs say ACCEPTABLE
civ_id=775 Ave Apalě Ethics don't match!
[ENTITY:FOREST]
[ETHIC:KILL_NEUTRAL:REQUIRED]
RAWs say ACCEPTABLE
Here, you can see that all five of these civs are now
REQUIRED to
KILL_NEUTRAL, while the former four also are
REQUIRED to
KILL_ENEMY.
I just did a quick mod so that civilization names are printed, so people can do research in Legends to see what had happened to cause such a change. Meanwhile, civilization IDs are also printed so that it is easier to locate them when hacking.