找回密码
 注册
X系列官方授权正版
搜索
查看: 1863|回复: 2

[转帖] X3逼降算法

[复制链接]
发表于 2018-3-25 17:08:03 | 显示全部楼层 |阅读模式
本帖最后由 vmake 于 2018-3-26 10:51 编辑

翻译自https://forum.egosoft.com/viewtopic.php?p=2342693#2342693
根据原贴,该作者根据某俄罗斯玩家的反编译成果从俄文翻译而来。
感谢Macaulish的提醒,已在该贴基础上根据源码做出诸多修正。LZ不是专业搞编程的,欢迎各位大佬指导。

1.船壳小于87%才可能投降。

2.逼降检查点间隔时间以30s为初值,逐渐翻倍。
如果每到检查点目标都恰好在被玩家攻击(即没有冗余时间),可能逼降成功的时间是0,30,60,120,240,480s......
当然目标不是时刻都在被攻击的,因此,检查点更有可能是0,35,65,133,253,493s......这种样子

3.每过30s,逼降概率降低1%。
结合第2条,战斗时间达到0,30,60,120,240,480s时,概率已经降低0%,1%, 2%, 4%, 8%, 16%。
这意味着第一次逼降是最有可能成功的

4.在和上次逼降检查点间隔超过300s之后的新攻击行为,会导致逼降检查点重置
300s基本意味着脱离战斗。

5.初始逼降概率=30%-士气值%。
野生的L士气值应该都是25。做任务可能刷出来比较低的。

6.目标船壳剩余
<25%时,投降概率+1%
<12%时,投降概率+2%
<8%时,投降概率+3%
<2%时,投降概率+10%
LZ捉Pirate Kestral的时候,目标士气值22。用5门Ion Shard Railgun一击把船壳降到0%,读档3次成功。

7.如果玩家的实力(护盾+船壳+最大武器伤害)<目标实力,投降概率降低5%。

8.如果目标是Khaak,概率值乘以“(玩家战斗等级 - 5) / 25”
这意味着要捉到Khaak的船,玩家战斗等级必须达到6级。否则概率为0。
只有玩家战斗等级达到30级,逼降Khaak船的概率才和逼降普通船一样。

9.概率算法:
如果当前概率>0到99的某一随机数,对方投降。

10.如果使用Ion Disruptor来逼降,概率乘以0.04
LZ很久以前试过。用ID烧Goner Ranger,烧到差不多40%船壳的时候才投降。要知道Goner Ranger的士气值都是很低的。


LZ一共读了大约10次档捉到了L交任务。每次只开一轮炮,不投降读档。
论坛里各位大佬根据经验总结的方法基本都是正确的,事实上我是捉到L以后才看到这个算法的。。


附ULiX反编译所得算法供大家研究。
  1. TSHIP.AttackedBy(Attacker, arg2) {
  2. //arg2 - судя по контексту тип применяемого оружия

  3. TSCRIPT.AttackedBy(Attacker,arg2);

  4. if (Attacker) {
  5. AttackerEv = SA_GetEventObject(Attacker);
  6. if (AttackerEv && sh_Owner){

  7. if (arg2 == 0 && (sh_Flags & 1)){ // если не использовано орудие
  8. TCLIENT.NotifyCollisionWarn(0); // угроза столкновения уровня 0
  9. } //L000BF866


  10. if ((ga_PlayerShip != AttackerEv) && (arg2) && (SA_GetFlag2(arg2) & 0100h)){
  11. //Если атакующий не корабль игрока, но использовано орудие, и орудие
  12. //способно на захват, то
  13. //при этом атакующим объектом становится корабль игрока, а атакующим - игрок, причем
  14. //только если он в активном секторе. Это условие необходимо для проверки орудий кокпитов.
  15. AttackerEv = ga_PlayerShip;
  16. Attacker = ga_PlayerShip.GetObjectID(); //функция вернёт нуль, если объект не в активном секторе
  17. } //L000BF8D4

  18. AttackerOwner = AttackerEv.GetOwner(); // (loc2) Принадлежность атакующего
  19. if (SE_IsClass(AttackerEv,2004){ //Атакующий объект является кораблем

  20. if (AttackerEv == ga_PlayerShip) || (THIS.IsEnemy(AttackerEv)){
  21. //Атакующий объект корабль игрока или враг
  22. //L000BF922 -> L000BF934

  23. if (AttackerOwner != sh_Owner){
  24. // Атакующий и атакуемый не принадлежат одному владельцу

  25. if !(arg2) || (SA_GetGroup(arg2) == AttackerOwner.GetGroup()) {
  26. //для атаки не использовано орудие или ......................
  27. //захват возможен только при данном
  28. //условии (SA_GetGroup(arg2) == AttackerOwner.GetGroup())
  29. //так как по следующему условию обязательно arg2 <> 0
  30. //L000BF9A0

  31. if (AttackerEv == ga_PlayerShip) && arg2 {
  32. //Атакующий объект - карабль игрока и применяет орудие
  33. //L000BF9CA:

  34. if !(TCLIENT.GetClientState() & 0200h) &&
  35. (sh_Owner <> ga_Player) && (sh_Owner <> ga_Races[18]){ // sh_Owner != Earth
  36. //сброшен 9 бит во флаге статистики игрока,
  37. //атакуемый не принадлежит игроку и землянам

  38. if ((SE_Random(100) < 4) || (SA_GetType(arg2) <> 17){ //Ion Distructor
  39. //Вот интересный момент. Если пользуемся ID то основное
  40. //вычисления вероятности возможно с вероятностью 0.04.
  41. //Если пользоваться не ID, то лишняя случайность отсекается
  42. //Использование ID уменьшает общую вероятность захватить в 25 раз!!!

  43. if (SA_GetShipTypeCockpitBody(sh_SubType) && !(sh_Flags & 0100b)){
  44. //если есть кабина и разрешен захват данного корабля

  45. Shield = SA_GetShield(sh_ObjectID);
  46. LimShield = SA_GetMaxShield(sh_ObjectID);
  47. Hull = SA_GetHul(sh_ObjectID);
  48. MaxHull = SA_GetMaxHull(sh_ObjectID);
  49. LimShield = LimShield / 100 // LimShield = MaxShield / 100
  50. if (LimShild > 1000) LimShild = 1000;
  51. //Минимальное значение щита, при котором
  52. //будет проверяться вероятность капитулирования
  53. // 1% от максимума, или 1000, если 1% превышает 1000 единиц.

  54. if (Shield <= LimShield) && (Hull > 0) && (Hull < SE_Random(8) * MaxHull / 8) {
  55. //Щиты меньше лимитного, а корпус поврежден и лежит в интервале от
  56. //нуля до переменного случайного значения MaxHull * i/8,
  57. //где i - целое в интервале от 0 до 7
  58. //Максимальная целостность корпуса, при которой возможен захват 87%

  59. if (ga_Player.GetAge() > sh_NextTakeoverChanceTime) {
  60. //Время игры превышает время, когда даётся шанс на захват

  61. if ((sh_TakeoverDelayTime > 300) &&
  62. (ga_Player.GetAge() > (sh_NextTakeoverChanceTime + sh_TakeoverDelayTime)) {
  63. //Задержка между шансами захватить больше 300 секунд, и
  64. //время игры больше времени следующего шанса + задержка между шансами

  65. sh_TakeoverDelayTime = 30; //Установить задержку в 30 секунд
  66. // -> L000BFBFC
  67. } //L000BFBD6 -> L000BFBEC
  68. else {
  69. sh_TakeoverDelayTime += sh_TakeoverDelayTime; //иначе, удвоить задержку
  70. }
  71. //L000BFBFC:
  72. sh_NextTakeoverChanceTime = ga_Player.GetAge() + sh_TakeoverDelayTime;
  73. //установить время следующего шанса захватить кораблик

  74. ProbabilityCapture = 30 - this.GetPilotMorale();
  75. //установить начальную вероятность захвата в 30% минус уровень марали атакуемого
  76. // т.е. от 20 до 30%

  77. if (AttackerEv.GetStrength() < this.GetStrength()){
  78. //если сила атакующего меньше силы атакуемого

  79. ProbabilityCapture -= 5;
  80. //уменьшить вероятность прыжка на 5%
  81. //сила находится как сумма Shileld + Hull + MaxLaserDamage
  82. }

  83. if (Hull <> 0){
  84. //Если корпус не равен нулю

  85. ProbabilityAdd = MaxHull / (Hull * 4);
  86. if (ProbabilityOnHull > 10) ProbabilityOnHull = 10;
  87. ProbabilityCapture += ProbabilityAdd;
  88. //Добавочная вероятность, по побитому корпусу
  89. //1% при целостности меньше 25%
  90. //2% - меньше 12%
  91. //3% - меньше 8%
  92. //10% - меньше 2%
  93. // Не стоящий учета параметр, главное, чтоб корпус был поврежден до
  94. // состояния 87%, когда вообще появляется шанс захватить
  95. } //L000BFC94

  96. ProbabilityCapture -= sh_TakeoverDelayTime / 30;
  97. //Уменьшение вероятности в зависимости от интервала ожидания между
  98. //шансами захватить
  99. //Возможные значения 1%, 2%, 4%, 8%, 16%, для 30, 60, 120, 240, 480 секунд задержки
  100. //Что объясняет больший шанс захватить сразу, нежели спустя некоторое время

  101. if (sh_Owner == ga_Races[7]){
  102. //Раса корабля хаак. Расчет вероятности захвата хааков
  103. //зависит от текущего боевого рейтинга.
  104. //до 6 уровня захват хааков невозможен, и только на 30 уровне
  105. //их можно захватывать наравне с другими кораблями
  106. //Множитель (FightRank - 5) / 25

  107. ProbabilityAdd = GetTitleFromFightRanking(ga_Player.GetFightRanking());
  108. ProbabilityCapture *= (ProbabilityAdd - 5) / 25;

  109. if (ProbabilityCapture < 0) ProbabilityCapture = 0;
  110. //если получилось меньше нуля, приравнять к нулю

  111. }//L000BFD06
  112. //
  113. if (ProbabilityCapture > SE_Random(100){
  114. //Ну и, наконец, если текущая вероятность больше случайного числа от 0 до 99
  115. //начинаем процесс захвата

  116. try
  117. {
  118. if (sh_Owner <> ga_Races[7]) && //Если шип не принадлежит расе хааков и
  119. (sh_Owner <> ga_Races[6]){ //и расе ксенонов,
  120. this.SendMessageSync(28); //то произнести фразу о сдаче корабля
  121. }
  122. else goto L000BFDBA;

  123. //К чему этот блок, я так и не понял, всё равно он пропущен, да и не могут эти расы кричать
  124. //Другого варианта структуры, кроме как безусловный переход, чтобы была
  125. // возможность не выполнить данный блок, я не нашёл, а он действительно не выполняется.

  126. if (sh_Owner == ga_Races[7]) || //Если шип не принадлежит расе хааков и
  127. (sh_Owner == ga_Races[6]){ //и расе ксенонов,
  128. this.SendMessageSync(28); //то произнести фразу о сдаче корабля
  129. }
  130. L000BFDBA:
  131. ga_Player.IncStatCounter(5080,1); //Увеличить число в статистике

  132. this.GetOwner().ChangeNotorietyKill(this,AttackerEv);
  133. //
  134. // Здесь мог ошибиться, привожу исходный код.
  135. //
  136. // push SP[6] ; loc1 ; // AttackerEv
  137. // get_object
  138. // push 2 //число аргументов для ChangeNotorietyKill
  139. // push 0 //число аргументов для GetOwner
  140. // get_object
  141. // call GetOwner ; 00000843 ; // GetOwner()
  142. // call ChangeNotorietyKill ; //ChangeNotorietyKill(arg1,arg2)
  143. //
  144. this.SetRaceLogicControl(1);
  145. this.LeaveShip(); //покидаем кораблик
  146. } catch(...){};
  147. } //L000BFE0C
  148. }
  149. }
  150. }
  151. }
  152. }
  153. }
  154. //<L000BFE14>
  155. //Далее процесс расчета повреждений,
  156. //который меня не сильно интересует
  157. //
  158. if (arg2){
  159. MaxLaser = AttakerEv.GetMaxLaser() + 10;
  160. this.MakeDamage(SE_Random(MaxLaser) + 10, 0);
  161. //Оружие наносит повреждения от 10 до 110% своей максимальной мощности

  162. } //L000BFE50

  163. if (AttackerOwner == ga_Player) && (AttackerEv <> ga_PlayerShip) &&
  164. (AttackerEv <> sh_Attacker){
  165. //Атакующий принадлежит игроку, не является кораблем, пилотируемым игроком
  166. //и не является предыдущим атакующим

  167. if (ga_Player.GetAge() > ga_Player.GetLastNotorietyHitTime() + 60){
  168. //От последнего попадания прошло больше минуты

  169. sh_Owner.AddNotoriety(ga_Player,-1);
  170. ga_Player.SetLastNotorietyHitTime(ga_Player.GetAge());
  171. //За каждую минуту атаки известность у определённой расы будет падать на 1 единицу
  172. }
  173. } //L000BFE78 -> L000BFE98 -> L000BFEF6

  174. ShipAttacker = sh_Attacker; //loc3
  175. LastTimeAttacked = sh_LastTime_Attacked; //loc4
  176. AutopilotOn = 1; //loc5
  177. if (sh_Flags & 1) {
  178. if(arg2){
  179. this.SetAttacker(AttackerEv);
  180. loc5 = this.IsAutopilotActive();
  181. } //L000BFF3E
  182. else AutopilotOn = 0; //loc5
  183. } //L000BFF4C
  184. else this.AttackedByHandler(Attacker,arg2);
  185. this.GetAttacker(); //Непонятный вызов процедуры, значение всё равно не сохраняется
  186. //
  187. //L000BFF60: push 0 //число аргументов для GetAttacker
  188. // get_object //this
  189. // call GetAttacker ; 00003A50
  190. // pop //теряем результат вызова функции
  191. // push SP[0] ; loc5
  192. //
  193. if (AutopilotOn && sh_Attacker){
  194. if ((sh_LastTime_Attacked - LastTimeAttacked) > 1){
  195. loc6 = 0;
  196. if (this.IsLanding() && sh_CollisionIgnoreObj){
  197. if (SA_FindObject(sh_CollisionIgnoreObj)){
  198. if !(SE_IsClass(sh_Attacker,2014)){ //Attacker Is Not Gate
  199. if (SA_GetMainType(sh_CollisionIgnoreObj) == 18) loc6 = 3; //SSTYPE_SHIP секторальный шип
  200. else loc6 = 2;
  201. } //L000C0022 -> L000C0056
  202. } //L000BFFFA -> L000C0022 -> L000C0056
  203. } //L000BFFD8 -> L000BFFFA -> L000C0022 -> L000C0056
  204. //L000C0056:
  205. this.Signal_Attacked(sh_Attacker,loc6)
  206. }//L000BFFAC -> L000C006E
  207. }//L000BFF88 -> L000C006E
  208. if !(sh_Flags & 01h) && (sh_Owner == ga_Player) &&
  209. !(SE_IsClass(this,2067)) { //ship is class TFIGHTDRONE
  210. TCLIENT.NotifyAttacked(this);
  211. }//L000C00B8
  212. } //L000BF990 -> L000BF99E -> L000C00BC
  213. } //L000BF952 -> L000BF99E -> L000C00BC
  214. } //L000BF924 -> L000BF932 -> L000BF952 -> L000BF99E -> L000C00BC
  215. } //L000BF932 -> L000BF952 -> L000BF99E -> L000C00BC
  216. } //L000C00C0
  217. } //L000C00C4
  218. return 0;
  219. }
复制代码
发表于 2018-3-25 18:44:11 | 显示全部楼层
本帖最后由 Macaulish 于 2018-3-25 18:47 编辑

你说的是原版的么?如果对于原版来说的话,基本都是准确的,但是有一点我觉得不太靠谱。

话说原版战斗等级30级能逼降大型战舰?M1、M2?我向来是战斗党,当年玩原版的时候,不说被我打爆的船了,就是被我抢过的船也有上千艘,豪不夸张,但是从来没有逼降过一艘战舰级别的船(当然是除了官方脚本没有装其他任何MOD脚本的情况下)。。。不知道有没有人成功过。。。我感觉原版是肯定不行的。然而目前玩的XTC里可以逼降大型战舰,而且不必战斗满级也可以。

另外抓侦察机,我觉得用带炮塔的M3效率比较高。比如你说的抓海盗隼这种高速小飞机,先干掉它所在编队的长机,然后停船等着它冲过来攻击你,这种状态下他的回避效率是最低的,武器一般用PAC这种威力和射程都比较适中的武器,像PBE虽然伤害高,但是攻击距离近,除非你的船在速度上能追着它打,不然效果不佳,至于你说的ISR,我要是没记错,原版里他是M6级别才能用的主炮级别武器吧。。。就M6这速度和机动效率,还有这武器威力,怎么控制输出伤害?我感觉要么就是打不到,要么就是轰成渣。。。你是怎么做到的?
回复

使用道具 举报

 楼主| 发表于 2018-3-26 09:51:35 | 显示全部楼层
Macaulish 发表于 2018-3-25 18:44
你说的是原版的么?如果对于原版来说的话,基本都是准确的,但是有一点我觉得不太靠谱。

话说原版战斗等级 ...

是原版。
原帖好像确实有一些不大合理的地方。。可能是翻译问题(原作者的或者我的)
待我好好研究一下俄罗斯人给的源码再做修正

我开的超速亥伯龙。Poisoned Paranid开局,反复读档直到刷出250.3m/s的,然后全宇宙的加速盒子全吃了,最终加速到372.0m/s。
大炮的好处是,如果算好了伤害配对武器,一次攻击(真的只按一次Ctrl)可以直接船壳见底。这样伤害控制很稳定,对于SL来说很重要。就是计算伤害配武器稍微麻烦点。
我以前玩TC的时候还试过用TrueLightSeeker配FAA捉船。。。。

特别的,对于M3,8门ISR也不是那种可以一击血槽见底的。但也就打个2秒钟吧,这个看手感了。我的L是这么捉来的。。
回复

使用道具 举报

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

本版积分规则

关闭

站长推荐上一条 /1 下一条

Archiver|手机版|小黑屋|DeepTimes.NET 太空游戏站