设为首页收藏本站

塞爱维(CIV)文明联盟

 找回密码
 注册
查看: 48762|回复: 35

[原创] [吃DLL长姿势系列 第二弹]抛砖引玉 - 文明5宗教系统数值解析

[复制链接]
发表于 2012-11-8 20:59:42 | 显示全部楼层 |阅读模式
前言不说太多。
开源之前关于宗教机制的讨论似乎各种各样,但是很遗憾的是,似乎没有人猜中……
这楼主要分析宗教建立和传播的机制,以及异端审判官和大预言家的工作原理,最后是信仰产生伟人的规则。
实际上,宗教传播的原理搞清楚之后,剩下的东西都挺简单的了。
有失误之处欢迎指出。

目录:
2楼 – 太长不看党的福音——所有结论一览
(真的没有上一篇的长,你信不信?信不信?信不信?)
3楼 – 核心机制:积累压力决定信教人口
4楼 – 改变积累压力的时机:人口改变,建立神系,建立宗教和自然传播
5楼 – 异端审判和大预言家
6楼 – 大预言家产生的时机,以及购买伟人的费用

[ 本帖最后由 object022 于 2012-11-8 21:01 编辑 ]

评分

1

查看全部评分

 楼主| 发表于 2012-11-8 20:59:54 | 显示全部楼层
【核心机制】
要理解下面内容,必须先明白有一个宗教叫做无信仰
宗教压力是累积的,宗教压力的比例决定城市各个宗教的信教人数
人口增加时一次性增加当前城市的主流信仰100点压力(主流信仰可以是无信仰)
神系创立时所有城市每人口增加200压力,会随着宗教传入被逐渐抵消
宗教创立时圣城一次性获得每人口500压力,并获得每回合30压力
【宗教单位】
传教士就是给城市一次性加上宗教压力(Strength)
异端审判官能够清除除了无宗教和本文明宗教以外宗教的累积压力
大先知能够同时完成以上两项

真的很短。

[ 本帖最后由 object022 于 2012-11-8 21:01 编辑 ]

评分

1

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-8 21:00:07 | 显示全部楼层
关于宗教的信教人口是怎么决定的,一向是讨论众多。
我们先来确认几个之前就明了的概念:
宗教压力:信教城市(信教人口大于城市人口的一半)会向周围10格的城市释放6点指定的宗教压力
强化信条中的巡回传教会将10格扩展为13格,宗教团结会让向友好城邦传教时的压力加倍,宗教文本会让压力变为8(10)点
圣城会对自己释放30点宗教压力。
这里的6定义在Speeds.xml下面(也就是说这个值和游戏速度有关),10定义在Globals.xml的RELIGION_ADJACENT_CITY_DISTANCE下面,圣城压力是普通城市的5倍,这个值也定义在Global下de。
注意一个很重要的概念:宗教压力是累积的。对于一个指定的宗教,每回合的累积值,就是其他该教城市向该城市释放的宗教压力之和,如果是圣城再加上30点标准压力。
现在我们引入一个新的宗教:“无信仰”。这个宗教没有任何压力,只是作为初始值而已。
之后,可以来看代码了。(我删除了一些边界判断,以及改宗的代码):
  1. void CvCityReligions::RecomputeFollowers(CvReligiousFollowChangeReason eReason, ReligionTypes eOldMajorityReligion, PlayerTypes eResponsibleParty)
  2. {
  3.         int iOldFollowers = GetNumFollowers(eOldMajorityReligion);
  4.         int iUnassignedFollowers = m_pCity->getPopulation();
  5.         int iPressurePerFollower;

  6.         // Find total pressure
  7.         int iTotalPressure = 0;
  8.         ReligionInCityList::iterator it;
  9.         for(it = m_ReligionStatus.begin(); it != m_ReligionStatus.end(); it++)
  10.         {
  11.                 iTotalPressure += it->m_iPressure;
  12.         }

  13.         iPressurePerFollower = iTotalPressure / iUnassignedFollowers;

  14.         // Loop through each religion
  15.         for(it = m_ReligionStatus.begin(); it != m_ReligionStatus.end(); it++)
  16.         {
  17.                 it->m_iFollowers = it->m_iPressure / iPressurePerFollower;
  18.                 iUnassignedFollowers -= it->m_iFollowers;
  19.                 it->m_iTemp = it->m_iPressure - (it->m_iFollowers * iPressurePerFollower);  // Remainder
  20.         }

  21.         // Assign out the remainder
  22.         for (int iI = 0; iI < iUnassignedFollowers; iI++)
  23.         {
  24.                 ReligionInCityList::iterator itLargestRemainder = NULL;
  25.                 int iLargestRemainder = 0;

  26.                 for (it = m_ReligionStatus.begin(); it != m_ReligionStatus.end(); it++)
  27.                 {
  28.                         if (it->m_iTemp > iLargestRemainder)
  29.                         {
  30.                                 iLargestRemainder = it->m_iTemp;
  31.                                 itLargestRemainder = it;
  32.                         }
  33.                 }

  34.                 if (itLargestRemainder && iLargestRemainder > 0)
  35.                 {
  36.                         itLargestRemainder->m_iFollowers++;
  37.                         itLargestRemainder->m_iTemp = 0;
  38.                 }
  39.         }
  40. }
复制代码
前几行累积了一个iTotalPressure,表示当前城市所有宗教的压力总和。
iPressurePerFollower指定了平均每个人口的宗教压力值。
接下来几行,给第I个宗教分布(iPressure / iPressurePerFollower)的信教人口,并保存余数。
最后把还没有分配的人口,按照余数从大到小的顺序,一个一个分配。
也就是说,城市的信教人口,大体上以积累的宗教压力为比例分配。

[ 本帖最后由 object022 于 2012-11-8 21:02 编辑 ]

评分

1

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-8 21:00:21 | 显示全部楼层
现在很多人可能在想:是不是说一个不信教的城市,自然传一次就直接全部信教了(因为这时候只有这一种宗教压力)?
答案是否定的……
  1. void CvCityReligions::DoPopulationChange(int iChange)
  2. {
  3.         ReligionTypes eMajorityReligion = GetReligiousMajority();

  4.         // Only add pressure if the population went up; if starving, leave pressure alone (but recompute followers)
  5.         if(iChange > 0)
  6.         {
  7.                 AddReligiousPressure(FOLLOWER_CHANGE_POP_CHANGE, eMajorityReligion, iChange * GC.getRELIGION_ATHEISM_PRESSURE_PER_POP());
  8.         }
  9.         else if (iChange < 0)
  10.         {
  11.                 RecomputeFollowers(FOLLOWER_CHANGE_POP_CHANGE, eMajorityReligion);
  12.         }
  13. }
复制代码
上面这段代码指定了当城市人口改变的时候会发生什么。
RELIGION_ATHEISM_PRESSURE_PER_POP是定义在Globals.xml里的常数,值为100,接下来还会用到,请记好。
一般我们不会饿死人口,所以就考虑人口增加的情况——每增加1个人口,直接增加当前城市信仰宗教100压力。
一开始,众所周知,城市的主流信仰就是无信仰。所以会给“无信仰”增加100压力。
这样就能解释新的宗教开始传播时,城市并非马上开始信仰该宗教了,因为还有存留的“无信仰”的压力值。
  1.         int iLoop;
  2.         CvCity* pLoopCity;
  3.         for(pLoopCity = kPlayer.firstCity(&iLoop); pLoopCity != NULL; pLoopCity = kPlayer.nextCity(&iLoop))
  4.         {
  5.                 // Add enough pressure to make this the likely majority religion
  6.                 pLoopCity->GetCityReligions()->AddReligiousPressure(FOLLOWER_CHANGE_PANTHEON_FOUNDED, newReligion.m_eReligion, GC.getRELIGION_ATHEISM_PRESSURE_PER_POP() * pLoopCity->getPopulation() * 2);
  7.         }
复制代码
上面这段代码是创立神系时的处理。这里我们可以看到,神系的创立就是在所有城市里给神系加上了200*人口的压力。
如果城市之前没有被传过教,大约会有2/3的人信仰这个神系。
  1.         iInitialPressure = GC.getRELIGION_INITIAL_FOUNDING_CITY_PRESSURE() * m_pCity->getPopulation();
  2.         CvReligionInCity newReligion(eReligion, true, 0, iInitialPressure);
  3.         m_ReligionStatus.push_back(newReligion);
复制代码
而创立宗教的处理更为简单:RELIGION_INITIAL_FOUNDING_CITY_PRESSURE定义在XML里,值为500,也就是说直接给圣城加上500*人口的压力。
加强宗教时对各城的压力没有做改变。
最后我们来看一个函数CvCityReligions::AddReligiousPressure的一段,增加宗教压力的操作大多会调用这个过程:
  1.         for(it = m_ReligionStatus.begin(); it != m_ReligionStatus.end(); it++)
  2.         {
  3.                 if(it->m_eReligion == eReligion)
  4.                 {
  5.                         it->m_iPressure += iPressure;
  6.                         bFoundIt = true;
  7.                 }

  8.                 //  If this is pressure from a real religion, reduce presence of pantheon by the same amount
  9.                 else if(eReligion > RELIGION_PANTHEON && it->m_eReligion == RELIGION_PANTHEON)
  10.                 {
  11.                         it->m_iPressure = max(0, (it->m_iPressure - iPressure));
  12.                 }
  13.         }
复制代码
这段代码里唯一需要注意的是后面几行,也就是说当宗教传入的时候,如果还有神系的压力,会自动被宗教压力抵消掉。
时代的眼泪啊。

[ 本帖最后由 object022 于 2012-11-8 21:02 编辑 ]

评分

1

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-8 21:00:36 | 显示全部楼层
现在我们可以很容易地解释传教士做的事情了:在目标城市里一次性增加1000宗教压力。有了上面的理论基础,不难理解这个行为。
宗教审判官呢?看代码:
  1. /// Remove other religions in a city (used by Inquisitor)
  2. void CvCityReligions::RemoveOtherReligions(ReligionTypes eReligion, PlayerTypes eResponsiblePlayer)
  3. {
  4.         ReligionTypes eOldMajorityReligion = GetReligiousMajority();

  5.         // Copy list
  6.         ReligionInCityList tempList;
  7.         ReligionInCityList::iterator it;
  8.         for(it = m_ReligionStatus.begin(); it != m_ReligionStatus.end(); it++)
  9.         {
  10.                 tempList.push_back(*it);
  11.         }

  12.         // Erase old list
  13.         m_ReligionStatus.clear();

  14.         // Recopy just what we want to keep
  15.         for(it = tempList.begin(); it != tempList.end(); it++)
  16.         {
  17.                 if(it->m_eReligion == NO_RELIGION || it->m_eReligion == eReligion)
  18.                 {
  19.                         m_ReligionStatus.push_back(*it);
  20.                 }
  21.         }

  22.         RecomputeFollowers(FOLLOWER_CHANGE_REMOVE_HERESY, eOldMajorityReligion, eResponsiblePlayer);
  23. }
复制代码
前几行枚举了当前城市拥有的宗教列表。
倒数几行的循环里有一句判断:                if(it->m_eReligion == NO_RELIGION || it->m_eReligion == eReligion)
也就是说,只保留当前宗教以及无宗教的压力,其他宗教的压力均被直接抹除。(注意还有无宗教。)
现在知道异端审判官为什么那么贵了吧,后期刷宗教,杠杠的。当然如果本来周边城市就都是异端的话,异端审判官也救不了你了……
那么大先知呢?
  1. void CvCityReligions::AddProphetSpread(ReligionTypes eReligion, int iPressure, PlayerTypes eResponsiblePlayer)
  2. {
  3.         int iAtheismPressure = 0;
  4.         int iReligionPressure = 0;
  5.         ReligionTypes eOldMajorityReligion = GetReligiousMajority();
  6.         ReligionTypes eHolyCityReligion = NO_RELIGION;
  7.         bool bProphetsReligionFoundedHere = false;

  8.         ReligionInCityList::iterator it;
  9.         for(it = m_ReligionStatus.begin(); it != m_ReligionStatus.end(); it++)
  10.         {
  11.                 if (it->m_eReligion == NO_RELIGION)
  12.                 {
  13.                         iAtheismPressure = it->m_iPressure;
  14.                 }
  15.                 else if (eReligion == it->m_eReligion)
  16.                 {
  17.                         iReligionPressure = it->m_iPressure;
  18.                         if (it->m_bFoundedHere)
  19.                         {
  20.                                 bProphetsReligionFoundedHere = true;
  21.                         }
  22.                 }
  23.                 else if (it->m_bFoundedHere)
  24.                 {
  25.                         eHolyCityReligion = it->m_eReligion;
  26.                 }
  27.         }

  28.         // Clear list
  29.         m_ReligionStatus.clear();

  30.         // Add atheists and this back in
  31.         CvReligionInCity atheism(NO_RELIGION, false/*bFoundedHere*/, 0, iAtheismPressure);
  32.         m_ReligionStatus.push_back(atheism);
  33.         CvReligionInCity prophetReligion(eReligion, bProphetsReligionFoundedHere, 0, iReligionPressure + iPressure);
  34.         m_ReligionStatus.push_back(prophetReligion);

  35.         // Reestablish Holy City religion
  36.         if (eHolyCityReligion != NO_RELIGION && !bProphetsReligionFoundedHere)
  37.         {
  38.                 CvReligionInCity holyCityReligion(eHolyCityReligion, true/*bFoundedHere*/, 0, 0);
  39.                 m_ReligionStatus.push_back(holyCityReligion);
  40.         }

  41.         RecomputeFollowers(FOLLOWER_CHANGE_PROPHET, eOldMajorityReligion, eResponsiblePlayer);
  42. }
复制代码
大先知做的事情其实就是异端审判+传教士。最后几行是判断洗了对方宗教圣城后把对应的宗教加回该城市信仰列表里(累积压力为0,但是圣城本身的30点并没有被洗掉),是为了应付一些需要判断圣城的情况。
因此所谓大先知能直接把别人圣城洗到不能翻身,也是不对的。

[ 本帖最后由 object022 于 2012-11-8 21:03 编辑 ]

评分

1

查看全部评分

回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-8 21:00:49 | 显示全部楼层
本来没必要说这个的,实在是因为这个写法太xD了,放出来给大家看看。
  1. /// How much does this prophet cost (recursive)
  2. int CvGameReligions::GetFaithGreatProphetNumber(int iNum) const
  3. {
  4.         int iRtnValue = 0;

  5.         if(iNum >= 1)
  6.         {
  7.                 if(iNum == 1)
  8.                 {
  9.                         iRtnValue = GC.getRELIGION_MIN_FAITH_FIRST_PROPHET();
  10.                 }
  11.                 else
  12.                 {
  13.                         iRtnValue = (GC.getRELIGION_FAITH_DELTA_NEXT_PROPHET() * (iNum - 1)) + GetFaithGreatProphetNumber(iNum - 1);
  14.                 }
  15.         }

  16.         return iRtnValue;
  17. }
复制代码
这里RELIGION_MIN_FAITH_FIRST_PROPHET=200,RELIGION_FAITH_DELTA_NEXT_PROPHET=100。
也就是说第I个大先知的费用=第I-1个大先知的费用+100(i-1),第1个的费用是200。
类似的有第I个伟人的费用=第I-1个伟人的费用+500(i-1),第1个的费用是1000(不贴代码了,没什么意义)
为什么我故意放这个呢?
因为它是用递归写的……汗。

最后来说下产生大预言家的时机。在bool CvGameReligions::CheckSpawnGreatProphet(CvPlayer& kPlayer)下,只截取核心部分。
  1.         if(iFaith < iCost)
  2.         {
  3.                 return false;
  4.         }
  5.         int iChance = GC.getRELIGION_BASE_CHANCE_PROPHET_SPAWN();
  6.         iChance += (iFaith - iCost);

  7.         int iRand = GC.getGame().getJonRandNum(100, "Religion: spawn Great Prophet roll.");
  8.         if(iRand >= iChance)
  9.         {
  10.                 return false;
  11.         }
复制代码
RELIGION_BASE_CHANCE_PROPHET_SPAWN定义在Global下,值为5。
这个的意思是,每回合产生大预言家的概率是(5+X),X为溢出的信仰。
恩,看起来很不科学嘛。

[ 本帖最后由 object022 于 2012-11-8 21:04 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-8 21:01:04 | 显示全部楼层
关于宗教的讨论似乎永远聚焦在信条上。
不是三个和金钱有关的创立者信条哪个好,就是各个宗教建筑的好坏,不然就是在宗教文本和巡回布道中选哪个,最近的虔诚开门的讨论才开始提起神庙神祠+快乐的信条。
从之前的讨论看来,大家更偏向出宗教建筑信条,让工业时代前的宗教有地方花掉。宗教裁判官是啥?你说可以防止传宗教?工人卡位法听说过么?
因此这篇文章似乎也没什么人来关注吧…笑。

总之还是一句话,欢迎讨论。
Over.

[ 本帖最后由 object022 于 2012-11-8 21:05 编辑 ]
回复 支持 反对

使用道具 举报

发表于 2012-11-8 21:33:31 | 显示全部楼层
神作啊,怎么可能会没人关注呢?大概是不高亮很多人没看见吧
回复 支持 反对

使用道具 举报

发表于 2012-11-8 21:46:41 | 显示全部楼层
为什么我的解析都没加红加精待遇
回复 支持 反对

使用道具 举报

发表于 2012-11-8 21:51:29 | 显示全部楼层

回复 9# 的帖子

哪一个?我只看到一个没完成的。。
回复 支持 反对

使用道具 举报

发表于 2012-11-8 21:52:24 | 显示全部楼层

回复 10# 的帖子

好吧……确实没完成,大概只差一个函数了……
回复 支持 反对

使用道具 举报

发表于 2012-11-8 22:00:37 | 显示全部楼层
额。。。大先知出现竟然是(5+x)%,太不科学了,每次都要浪费好多信仰啊!!!
回复 支持 反对

使用道具 举报

发表于 2012-11-8 22:05:21 | 显示全部楼层
楼主给力!
看不懂代码的小白问些问题...
感觉一个没有其他宗教信徒的城市比有其他宗教信徒的城市更容易被传教士或大预感化,是不是作为一种宗教的“无宗教”和其他宗教有区别?
如果一个城市有神系的话,感化的机制是需要先抵消它的神系吗?感觉自己创教后新建的城比已有神系的城市更早有第一个宗教信徒
谢谢~
回复 支持 反对

使用道具 举报

发表于 2012-11-8 22:23:00 | 显示全部楼层
膜拜高人
送上经验魅力各一点
问几个问题
1:转化一人口需要的压力是多少,怎么决定1000压力转化的人口数
2:为什么在敌人大先知接近圣城时,买出审判官,敌人会自动退走? 是AI算法引起的吗?

最后问哈,这个是神马语言编写的?
回复 支持 反对

使用道具 举报

发表于 2012-11-8 22:24:59 | 显示全部楼层
总共评了5L的分,总计魅力5+经验5,请查收
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-8 22:57:54 | 显示全部楼层

回复 14# 的帖子

1 1000压力转化的人口数可以近似看成1000/(1000+X)*Pop,X是城市里存下的所有宗教压力值之和,POP是城市人口
2 当审判官在城市或其周围6格时,敌方的传教士和大先知不能在该城市传教,这是审判官的被动技能(主动技能就是洗城市啦)
其实高难度下貌似不怎么用这个单位的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-11-8 23:00:10 | 显示全部楼层

回复 13# 的帖子

根本原因是这个城市的累计宗教压力少,所以传教士传教效果就好
大先知的话不管怎么样效果都很好的,我几乎没看过大先知洗不过来的城市
神系的累计压力会通过传播宗教被自然洗掉,但是被完全洗掉之前的确会让传教速度变慢
这里用的是C++,实际上可以用任何语言去写(你想用机器码都行),对上接口就行了

[ 本帖最后由 object022 于 2012-11-8 23:02 编辑 ]
回复 支持 反对

使用道具 举报

发表于 2012-11-8 23:38:39 | 显示全部楼层
原帖由 object022 于 2012-11-8 23:00 发表
根本原因是这个城市的累计宗教压力少,所以传教士传教效果就好
大先知的话不管怎么样效果都很好的,我几乎没看过大先知洗不过来的城市
神系的累计压力会通过传播宗教被自然洗掉,但是被完全洗掉之前的确会让传教速 ...



大先知一般只能转换12人口,有时只能转换10人口。而传教士一次性只能转换5人口

[ 本帖最后由 hk1976 于 2012-11-8 23:39 编辑 ]
回复 支持 反对

使用道具 举报

发表于 2012-11-9 00:46:07 | 显示全部楼层

回复 16# 的帖子

建议把这部分在二楼详细的解释一下(这样举例挺好的),如果没看见这楼,光看二楼真理解不了压力-人口转换效率

另外(5+X)%这个无语的结果怎么不在结论里

[ 本帖最后由 CCX_CX_D 于 2012-11-9 00:47 编辑 ]
回复 支持 反对

使用道具 举报

发表于 2012-11-9 07:16:01 | 显示全部楼层
(5+x)%这个。。。这是码农做出来的事情嘛
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|塞爱维(CIV)文明联盟    

GMT+8, 2024-3-28 22:39

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表