AIPlayer.ts 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. import DataMgr from "../data/DataMgr";
  2. import { RESET_GAME } from "../data/Define";
  3. import TalkMgr from "../game/TalkMgr";
  4. import { UIGame } from "../UI/UIGame";
  5. import { xGame } from "../xGame";
  6. import D2BallSrc from "./D2BallSrc";
  7. import D2RootBallSrc from "./D2RootBallSrc";
  8. enum AIState {
  9. none = 0,
  10. idle, //闲置状态(对方击球)
  11. reset, //重置母球
  12. move, //移动母球
  13. wait, //
  14. select, //选择
  15. turn, //调整杆的角度
  16. shoot, //击球
  17. leave, //离线
  18. }
  19. export default class AIPlayer {
  20. private dtime: number = 30;
  21. private gameSrc: UIGame;
  22. private awayTime: number = 0;
  23. private awayNum: number = 0;
  24. private hideTime: number = 0;
  25. private rootMoveTime: number = 0;
  26. private isGuide: boolean = false;
  27. private tempDir1: Laya.Vector2;
  28. private tempDir2: Laya.Vector2;
  29. private tempDir3: Laya.Vector2;
  30. private state = AIState.none;
  31. private waitTime = 0;
  32. private forceNum = 0;
  33. private forceMax = 0;
  34. private ballSize = 33;
  35. //选取次数
  36. private selectNum = 0;
  37. //球杆旋转值
  38. private destRot = 0;
  39. //AI等级(1-10)
  40. private aiLv = 10;
  41. //可以打的球
  42. private kickBall: D2BallSrc[] = [];
  43. //可以直线打的球
  44. private lineBall: D2BallSrc[] = []
  45. //可以直接进袋的球
  46. private scoreBall: D2BallSrc[] = [];
  47. //与袋口没阻挡的球
  48. private holeBall: D2BallSrc[] = [];
  49. //调杆次数
  50. private turnNum: number = 0;
  51. private setNum: number = 0;
  52. private setMax: number = 0;
  53. private setRate: number = 0;
  54. //球边界坐标
  55. public rectL: number = 0;
  56. public rectR: number = 0;
  57. public rectT: number = 0;
  58. public rectB: number = 0;
  59. private wBall: D2RootBallSrc;
  60. private rootPos = { x: 0, y: 0 };
  61. private bestForce = 0;
  62. private bestAngle = 180;
  63. private bestPos = { x: 0, y: 0 };
  64. private bestKick = { x: 0, y: 0 };
  65. private posList = [];
  66. private posIdx: number = 0;
  67. private chatTime: number = 3000;
  68. constructor() {
  69. this.tempDir1 = new Laya.Vector2();
  70. this.tempDir2 = new Laya.Vector2();
  71. this.tempDir3 = new Laya.Vector2();
  72. Laya.timer.loop(this.dtime, this, this.onUpdate);
  73. xGame.eventMgr.off(RESET_GAME, this, this.resetAI);
  74. xGame.eventMgr.on(RESET_GAME, this, this.resetAI);
  75. this.gameSrc = xGame.common.gameUI;
  76. this.hideTime = Laya.timer.currTimer;
  77. Moyu.setOnTipCall((id) => {
  78. if (id == 20012)
  79. this.hideTime = Laya.timer.currTimer;
  80. else (id == 20011)
  81. {
  82. let dt = Laya.timer.currTimer - this.hideTime;
  83. console.log("onshow" + dt);
  84. if (dt > 30000 && this.state > AIState.none)
  85. xGame.common.gameMgr.aiGameEnd(false);
  86. }
  87. }, this)
  88. }
  89. public init() {
  90. let ggg = 1;
  91. if (DataMgr.getGuideDefaultModel() === 1) {
  92. ggg = DataMgr.getLianXiGuide();
  93. } else {
  94. ggg = DataMgr.getSkillGuide();
  95. }
  96. this.isGuide = ggg < 1 || DataMgr.gameData.gameCount < 1;
  97. this.awayTime = xGame.tools.random(5000, 10000);
  98. this.awayNum = 0;
  99. this.hideTime = Laya.timer.currTimer;
  100. let rect = xGame.common.d2World.borderRect;
  101. this.rectL = Math.ceil(rect.x) + 17;
  102. this.rectT = Math.ceil(rect.y) + 17;
  103. this.rectR = Math.floor(rect.x + rect.width) - 17;
  104. this.rectB = Math.floor(rect.y + rect.height) - 17;
  105. this.wBall = xGame.common.d2World.rootBall; //母球
  106. this.ballSize = this.wBall.gameObj.width - 1;
  107. if (this.posList.length < 1) {
  108. let dp = 40;
  109. for (let i = this.rectL; i < this.rectR + dp; i = i + dp) {
  110. for (let j = this.rectT; j < this.rectB + dp; j = j + dp) {
  111. let dx = i < this.rectR ? i : this.rectR;
  112. let dy = j < this.rectB ? j : this.rectB;
  113. let order = xGame.tools.random(0, 1000);
  114. this.posList.push({ x: dx, y: dy, order: order });
  115. }
  116. }
  117. this.posList.sort(function (a, b) {
  118. return a.order - b.order;
  119. });
  120. this.setMax = this.posList.length;
  121. }
  122. this.state = AIState.idle;
  123. }
  124. public setAiLevel(num) {
  125. this.aiLv = num;
  126. }
  127. //计算可打的球
  128. initBall() {
  129. //this.aiLv = xGame.tools.random(1, 11);
  130. //console.log("AI等级" + this.aiLv);
  131. this.rootPos.x = this.wBall.gameObj.x;
  132. this.rootPos.y = this.wBall.gameObj.y;
  133. this.initHoleBall();
  134. }
  135. initHoleBall() {
  136. this.kickBall = [];
  137. this.lineBall = []
  138. this.scoreBall = [];
  139. this.holeBall = [];
  140. this.selectNum = 0;
  141. this.forceNum = 0;
  142. this.forceMax = 0;
  143. this.turnNum = 0;
  144. let list = xGame.common.d2World.ballArr;
  145. if (list.length < 20 && !this.isGuide) //大招模式球太多时随机选择
  146. {
  147. let holes = xGame.common.gameMgr.holePosList;
  148. for (let i = 0; i < list.length; i++) {
  149. let obj = list[i];
  150. if (!obj)
  151. continue;
  152. //重置前面计算了的参数
  153. obj.kickPos.x = 0;
  154. obj.kickPos.y = 0;
  155. obj.poleAngle = 0;
  156. obj.hard = 0;
  157. obj.borderDis = 0;
  158. obj.kickAngle = 180;
  159. if (obj.number == 0) //母球
  160. continue;
  161. if (obj.ballType == xGame.common.gameMgr.selfCamp) //对方球
  162. continue;
  163. if (obj.number == 8 && xGame.common.gameMode == 0) {
  164. if (xGame.common.gameMgr.aiBallArr.length > 0 || !xGame.common.gameMgr.ensureBallType)
  165. continue;
  166. }
  167. let b = obj.gameObj;
  168. let wbp = this.rootPos;
  169. let bx = wbp.x - b.x;
  170. let by = wbp.y - b.y;
  171. obj.borderDis = xGame.tools.random(0, 100);
  172. this.kickBall.push(obj);
  173. //与母球之间有其他球
  174. if (!this.isLine(wbp, obj))
  175. continue;
  176. obj.kickAngle = xGame.tools.random(90, 180);
  177. obj.kickPos.x = b.x;
  178. obj.kickPos.y = b.y;
  179. this.lineBall.push(obj);
  180. let minDis = 999999;
  181. let hole = false;
  182. for (let j = 0; j < holes.length; j++) {
  183. let hp = holes[j];
  184. //袋口方向不对
  185. if (bx >= 0 && hp.x > b.x + 100)
  186. continue;
  187. if (bx <= 0 && hp.x < b.x - 100)
  188. continue;
  189. if (by < -10 && hp.y < wbp.y)
  190. continue;
  191. if (by > 10 && hp.y > wbp.y)
  192. continue;
  193. //与袋口之间没有球
  194. if (!this.isBlock(obj.number, b.x, b.y, hp.x, hp.y)) {
  195. let dis = xGame.tools.getDeltaxy(b.x, b.y, hp.x, hp.y);
  196. if (dis < minDis) {
  197. let dx = b.x - this.tempDir1.x * this.ballSize;
  198. let dy = b.y - this.tempDir1.y * this.ballSize;
  199. this.tempDir3.x = this.tempDir1.x;
  200. this.tempDir3.y = this.tempDir1.y;
  201. if (!this.isBlock(this.wBall.number, wbp.x, wbp.y, dx, dy, obj.number)) //进球路线上没有其他球
  202. {
  203. obj.kickPos.x = dx;
  204. obj.kickPos.y = dy;
  205. let num = Laya.Vector2.dot(this.tempDir1, this.tempDir3);
  206. if (num > 1)
  207. num = 1;
  208. obj.kickAngle = Math.acos(num) * 57.3;
  209. //库边球难度加大
  210. if (b.x < this.rectL + 2 || b.x > this.rectR - 2 || b.y < this.rectT + 2 || b.y > this.rectB - 2)
  211. dis = dis * 2;
  212. //中袋球难度加大
  213. if (Math.abs(hp.x - 600) < 100)
  214. dis += 50;
  215. obj.hard = dis + obj.kickAngle * 3;
  216. hole = true;
  217. }
  218. }
  219. }
  220. }
  221. if (hole) {
  222. this.scoreBall.push(obj);
  223. this.holeBall.push(obj);
  224. }
  225. }
  226. this.scoreBall.sort(function (a, b) {
  227. return a.hard - b.hard;
  228. });
  229. this.holeBall.sort(function (a, b) {
  230. return a.kickAngle - b.kickAngle;
  231. });
  232. this.kickBall.sort(function (a, b) {
  233. return a.borderDis - b.borderDis;
  234. });
  235. }
  236. else {
  237. let max = list.length - 1;
  238. let i = xGame.tools.random(0, max);
  239. for (i; i <= max; i++) {
  240. let idx = i >= max ? i -= max : i;
  241. let obj = list[idx];
  242. if (obj && obj.number != 0) {
  243. obj.kickAngle = xGame.tools.random(90, 180);
  244. obj.kickPos.x = obj.gameObj.x + xGame.tools.random(-10, 10);
  245. obj.kickPos.y = obj.gameObj.y + xGame.tools.random(-10, 10);
  246. this.lineBall.push(obj);
  247. break;
  248. }
  249. }
  250. }
  251. }
  252. selectBall() {
  253. let ball: D2BallSrc = null;
  254. this.destRot = 0;
  255. let max = this.isGuide ? 1 : 3 - Math.ceil(this.aiLv / 4) + xGame.tools.random(0, 2);
  256. //let wb=xGame.common.d2World.rootBall;
  257. let ai = 110 - this.aiLv * 10;
  258. let mis = xGame.tools.random(-ai, ai) * 0.005;
  259. let score = false;
  260. //没有可以直接进袋的球
  261. if (this.scoreBall.length < 1) {
  262. if (this.lineBall.length < 1) {
  263. for (let i = 0; i < this.kickBall.length; i++) {
  264. let list = [];
  265. let bo = this.kickBall[i];
  266. let bobj = bo.gameObj;
  267. let rect = xGame.common.d2World.borderRect;
  268. let mobj = this.wBall.gameObj;
  269. //上边库
  270. let minb = bobj.y - rect.y - 20;
  271. let minw = mobj.y - rect.y - 50;
  272. if (minb > 0 && minw > 0) {
  273. let dlenx = 0;
  274. let dleny = mobj.y - rect.y;
  275. let spx = mobj.x;
  276. let spy = rect.y - dleny;
  277. list.push({ order: minb, bx: 0, by: rect.y, spx: spx, spy: spy, dlenx: dlenx, dleny: dleny });
  278. }
  279. //下边库
  280. minb = rect.y + rect.height - bobj.y - 20
  281. minw = rect.y + rect.height - mobj.y - 50;
  282. if (minb > 0 && minw > 0) {
  283. let dlenx = 0;
  284. let dleny = rect.y + rect.height - mobj.y;
  285. let spx = mobj.x;
  286. let spy = rect.y + rect.height + dleny;
  287. list.push({ order: minb, bx: 0, by: rect.y + rect.height, spx: spx, spy: spy, dlenx: dlenx, dleny: dleny });
  288. }
  289. //左边库
  290. minb = bobj.x - rect.x - 20;
  291. minw = mobj.x - rect.x - 50;
  292. if (minb > 0 && minw > 0) {
  293. let dlenx = mobj.x - rect.x;
  294. let dleny = 0;
  295. let spx = rect.x - dlenx;
  296. let spy = mobj.y;
  297. list.push({ order: minb, bx: rect.x, by: 0, spx: spx, spy: spy, dlenx: dlenx, dleny: dleny });
  298. }
  299. //右边库
  300. minb = rect.x + rect.width - bobj.x - 20;
  301. minw = rect.x + rect.width - mobj.x - 50;
  302. if (minb > 0 && minw > 0) {
  303. let dlenx = rect.x + rect.width - mobj.x;
  304. let dleny = 0;
  305. let spx = rect.x + rect.width + dlenx;
  306. let spy = mobj.y;
  307. list.push({ order: minb, bx: rect.x + rect.width, by: 0, spx: spx, spy: spy, dlenx: dlenx, dleny: dleny });
  308. }
  309. //按距边排序
  310. list.sort(function (a, b) {
  311. return a.order - b.order;
  312. });
  313. for (let i = 0; i < list.length; i++) {
  314. let data = list[i];
  315. let mx = mobj.x - bobj.x;
  316. let my = mobj.y - bobj.y;
  317. xGame.tools.getV2Dir(data.spx, data.spy, bobj.x, bobj.y, this.tempDir1);
  318. xGame.tools.getV2Dir(data.spx, data.spy, mobj.x, mobj.y, this.tempDir2);
  319. let num = Laya.Vector2.dot(this.tempDir1, this.tempDir2);
  320. if (num > 1)
  321. num = 1;
  322. let cos = Math.acos(num);
  323. if (cos > 1.57)
  324. cos = 1.57;
  325. else if (cos < 0.01)
  326. cos = 0.01;
  327. let tan = Math.tan(cos);
  328. let dx = tan * data.dleny;
  329. let dy = tan * data.dlenx;
  330. let bx = data.bx > 0 ? data.bx : (mobj.x + (mx > 0 ? -dx : dx));
  331. if (Math.abs(bx - 667) < 40) //避开中袋
  332. continue;
  333. let by = data.by > 0 ? data.by : (mobj.y + (my > 0 ? -dy : dy));
  334. if (!this.isBlock(0, mobj.x, mobj.y, bx, by) && !this.isBlock(bo.number, bobj.x, bobj.y, bx, by)) {
  335. let dis = Math.abs(data.dlenx) + Math.abs(data.dleny) + xGame.tools.getDistance(bx, by, bo.gameObj.x, bo.gameObj.y);
  336. this.forceMax = dis * 0.08;
  337. this.destRot += mis * 0.1;
  338. this.setKick(bx, by);
  339. return true;
  340. }
  341. }
  342. }
  343. }
  344. else {
  345. ball = xGame.common.getRandArrEle(this.lineBall);
  346. }
  347. }
  348. else {
  349. for (let i = 0; i < this.scoreBall.length; i++) {
  350. if (i < this.scoreBall.length - 1) {
  351. let rand = 70 + this.aiLv * 10 + i * 10;
  352. if (xGame.tools.random(0, 100) < rand) {
  353. ball = this.scoreBall[i];
  354. break;
  355. }
  356. }
  357. else {
  358. ball = this.scoreBall[i];
  359. break;
  360. }
  361. }
  362. score = true;
  363. this.selectNum++;
  364. }
  365. if (!ball)
  366. ball = xGame.common.getRandArrEle(xGame.common.d2World.ballArr);
  367. //选取完成
  368. if (this.selectNum >= max) {
  369. console.log('zh:jian kong bug1');
  370. if (ball && ball.gameObj) {
  371. // 安全访问ball.gameObj的属性
  372. } else {
  373. console.log('zh:jian kong bug2 如果到这里应该能看到一个alert弹框报错');
  374. }
  375. let dx = ball.gameObj.x;
  376. let dy = ball.gameObj.y;
  377. let kickAngle = 0; //击球夹角
  378. if (score) {
  379. dx = ball.kickPos.x;
  380. dy = ball.kickPos.y;
  381. this.tempDir2 = xGame.tools.getV2Dir(this.wBall.gameObj.x, this.wBall.gameObj.y, dx, dy, this.tempDir2);
  382. this.tempDir3 = xGame.tools.getV2Dir(dx, dy, ball.gameObj.x, ball.gameObj.y, this.tempDir3);
  383. let adot = Laya.Vector2.dot(this.tempDir3, this.tempDir2);
  384. if (adot > 1)
  385. adot = 1;
  386. kickAngle = Math.acos(adot) * 57.3;
  387. if (kickAngle > 45) //大角度的失误不往球外偏移
  388. {
  389. let kdx = ball.kickPos.x - ball.gameObj.x;
  390. let kdy = ball.kickPos.y - ball.gameObj.y;
  391. dx = ball.kickPos.x - kdx * mis * 0.3;
  392. dy = ball.kickPos.y - kdy * mis * 0.3;
  393. }
  394. else
  395. this.destRot += mis;
  396. }
  397. else if (ball.poleAngle != 0) //插边球
  398. {
  399. this.destRot += ball.poleAngle;
  400. }
  401. if (xGame.common.gameMode == 0) {
  402. let af = kickAngle * 0.08;
  403. this.forceMax = ball.hard * 0.1 + af * af;
  404. }
  405. this.setKick(dx, dy);
  406. }
  407. else //模拟多次选择
  408. {
  409. let rand = this.aiLv * 5 + 40;
  410. if (xGame.tools.random(0, 100) < rand && this.scoreBall.length > 0)
  411. ball = xGame.common.getRandArrEle(this.scoreBall);
  412. else
  413. ball = xGame.common.getRandArrEle(xGame.common.d2World.ballArr);
  414. rand = this.aiLv * 3 + 40 + this.selectNum * 20;
  415. if (xGame.tools.random(0, 100) > rand) {
  416. this.selectNum = max;
  417. }
  418. this.setState(AIState.select, xGame.tools.random(500, 1000));
  419. this.selectNum++;
  420. }
  421. ball.onClick(0, true);
  422. }
  423. isBlock(bn, bx, by, hx, hy, dn = -1) {
  424. this.tempDir1 = xGame.tools.getV2Dir(bx, by, hx, hy, this.tempDir1);
  425. let balls = xGame.common.d2World.ballArr;
  426. let max = xGame.tools.getDis(hx, hy, bx, by);
  427. for (let index = 0; index < balls.length; index++) {
  428. let obj = balls[index];
  429. if (obj.number == 0) //母球
  430. continue;
  431. if (obj.number == bn || obj.number == dn) //自己
  432. continue;
  433. //其他球与该球的方向距离
  434. let dis = xGame.tools.getDistance(obj.gameObj.x, obj.gameObj.y, bx, by);
  435. // if(bn==0)
  436. // dis=obj.getDis(this.wBall);
  437. // else
  438. // dis=
  439. if (dis * dis > max)
  440. continue;
  441. this.tempDir2 = xGame.tools.getV2Dir(bx, by, obj.gameObj.x, obj.gameObj.y, this.tempDir2);
  442. let num = Laya.Vector2.dot(this.tempDir1, this.tempDir2);
  443. if (num > 1) num = 1;
  444. if (num < 0) //大于90
  445. continue;
  446. let gap = Math.sin(Math.acos(num)) * dis;
  447. if (gap < this.ballSize)
  448. return true;
  449. }
  450. return false;
  451. }
  452. //能否直接打到该球
  453. isLine(xy, ball: D2BallSrc) {
  454. let bx = xy.x;
  455. let by = xy.y;
  456. if (this.isBlock(0, bx, by, ball.gameObj.x, ball.gameObj.y, ball.number)) {
  457. let dis = ball.getDestDis(xy);
  458. let angle = Math.asin((ball.gameObj.width - 3) / dis);
  459. let sin = Math.sin(angle);
  460. let cos = Math.cos(angle);
  461. this.tempDir3 = xGame.tools.getV2Dir(bx, by, ball.gameObj.x, ball.gameObj.y, this.tempDir3);
  462. this.tempDir2.x = this.tempDir3.x * cos - this.tempDir3.y * sin;
  463. this.tempDir2.y = this.tempDir3.x * sin + this.tempDir3.y * cos;
  464. let dx = bx + this.tempDir2.x * dis;
  465. let dy = by + this.tempDir2.y * dis;
  466. let isr = this.isBlock(0, bx, by, dx, dy, ball.number);
  467. sin = Math.sin(-angle);
  468. cos = Math.cos(-angle);
  469. this.tempDir2.x = this.tempDir3.x * cos - this.tempDir3.y * sin;
  470. this.tempDir2.y = this.tempDir3.x * sin + this.tempDir3.y * cos;
  471. dx = bx + this.tempDir2.x * dis;
  472. dy = by + this.tempDir2.y * dis;
  473. let isl = this.isBlock(0, bx, by, dx, dy, ball.number);
  474. if (isr && isl)
  475. return false;
  476. ball.poleAngle = isr ? -angle * 57.3 : angle * 57.3;
  477. }
  478. return true;
  479. }
  480. //调整球杆
  481. turnPole() {
  482. //控制调整频率
  483. this.turnNum++;
  484. let rot = xGame.common.d2World.d2Gan.ui.rotation % 360;
  485. let dr = rot - this.destRot;
  486. let abs = Math.abs(dr) % 360;
  487. //不转大圈
  488. if (abs > 180) {
  489. if (rot > 180)
  490. rot -= 360;
  491. if (this.destRot > 180)
  492. this.destRot -= 360;
  493. }
  494. let max = this.turnNum * 5 + this.aiLv * 5;
  495. if (abs < 0.2 && xGame.tools.random(0, 100) < max) {
  496. xGame.common.d2World.d2Gan.rotDest(this.destRot);
  497. let at = xGame.tools.random(1000, 2000);
  498. if (!this.onAway(3))
  499. at += xGame.tools.random(10000, 15000);
  500. this.setState(AIState.shoot, at);
  501. //console.log("调杆"+this.turnNum+"次");
  502. }
  503. else {
  504. let mr = 0;
  505. if (abs > 3)
  506. mr = 1 + xGame.tools.random(2, 6) * abs * 0.01
  507. else if (Math.random() > 0.6)
  508. mr = xGame.tools.random(1, 10) * 0.1
  509. mr = dr > 0 ? -mr : mr;
  510. xGame.common.d2World.d2Gan.rotDest(rot + mr);
  511. //xGame.common.d2World.d2Gan.rotateSelf(mr);
  512. }
  513. }
  514. //击球方向、力
  515. setKick(dx, dy) {
  516. this.forceMax += this.wBall.getDestDis({ x: dx, y: dy }) * 0.1;
  517. this.tempDir2 = xGame.tools.getV2Dir(this.wBall.gameObj.x, this.wBall.gameObj.y, dx, dy, this.tempDir2);
  518. this.tempDir1.x = 0; this.tempDir1.y = 1;
  519. this.tempDir2.y = -this.tempDir2.y;
  520. let num = Laya.Vector2.dot(this.tempDir1, this.tempDir2);
  521. if (num > 1) num = 1;
  522. let rot = Math.acos(num) * 57.3;
  523. //0~360
  524. if (this.tempDir2.x < 0)
  525. rot = 360 - rot;
  526. this.destRot += rot;
  527. if (!xGame.common.gameMgr.finishOneShoot) //开球可能额外加力
  528. {
  529. this.forceMax += xGame.tools.random(10, 50);
  530. this.forceMax -= this.aiLv * 2; // 高级AI低概率大力开球
  531. if (this.forceMax < 20)
  532. this.forceMax = 20;
  533. }
  534. if (this.forceMax > 100)
  535. this.forceMax = 101;
  536. else if (this.forceMax < 10)
  537. this.forceMax += xGame.tools.random(0, 10);
  538. if (xGame.common.gameMode == 1)
  539. this.forceMax = xGame.tools.random(80, 100);
  540. else
  541. this.forceMax = xGame.tools.random(this.forceMax * 0.5, this.forceMax);
  542. let max = 2000 - this.aiLv * 100;
  543. if (Math.random() > 0.8)
  544. max += xGame.tools.random(100, 2000);
  545. this.setState(AIState.turn, xGame.tools.random(500, max));
  546. }
  547. //拉杆击球
  548. shoot() {
  549. if (this.waitTime < 0 && Math.abs(this.forceNum - this.forceMax) < 4) {
  550. if (Math.random() > 0.8) //拉到位后再有点小延迟
  551. {
  552. this.gameSrc.aiForce(1);
  553. this.setState(AIState.wait);
  554. }
  555. }
  556. else {
  557. let df = xGame.tools.random(3, 8);
  558. if (this.forceNum < this.forceMax)
  559. this.forceNum += df;
  560. else
  561. this.forceNum -= df;
  562. let startY = this.gameSrc.ui.powerNode.y;
  563. let height = this.gameSrc.ui.powerNode.height;
  564. //
  565. let force = startY + height * this.forceNum * 0.01;
  566. this.gameSrc.aiForce(2, force);
  567. }
  568. }
  569. reset() {
  570. for (let i = 0; i < 4; i++) {
  571. this.setRate--;
  572. this.setNum++;
  573. let pos = this.posList[this.posIdx];
  574. this.rootPos.x = pos.x + xGame.tools.random(-20, 20);
  575. this.rootPos.y = pos.y + xGame.tools.random(-20, 20);
  576. if (!xGame.common.gameMgr.isTouchedBall && this.rootPos.x > 387)
  577. this.rootPos.x = 387;
  578. if (this.rootPos.x < this.rectL)
  579. this.rootPos.x = this.rectL;
  580. else if (this.rootPos.x > this.rectR)
  581. this.rootPos.x = this.rectR;
  582. if (this.rootPos.y < this.rectT)
  583. this.rootPos.y = this.rectT;
  584. else if (this.rootPos.y > this.rectB)
  585. this.rootPos.y = this.rectB;
  586. this.posIdx++;
  587. let max = this.posList.length;
  588. if (this.posIdx >= max)
  589. this.posIdx -= max;
  590. this.initHoleBall();
  591. let ball = null;
  592. if (this.scoreBall.length > 0)
  593. ball = this.scoreBall[0];
  594. else if (this.lineBall.length > 0)
  595. ball = xGame.common.getRandArrEle(this.lineBall);
  596. if (ball && ball.kickAngle < this.bestAngle) {
  597. this.bestAngle = ball.kickAngle;
  598. this.bestKick.x = ball.kickPos.x;
  599. this.bestKick.y = ball.kickPos.y;
  600. this.bestPos.x = this.rootPos.x;
  601. this.bestPos.y = this.rootPos.y;
  602. this.bestForce = ball.hard * 0.08;
  603. }
  604. }
  605. if (this.setNum >= this.setMax)
  606. this.setMax = 0;
  607. if (this.setRate < 1) {
  608. this.setState(AIState.move, 0);
  609. this.rootMoveTime = Laya.timer.currTimer;
  610. this.gameSrc.aiRootMove(true);
  611. }
  612. }
  613. delayLine() {
  614. this.gameSrc.aiRootMove(false);
  615. }
  616. move() {
  617. if (this.bestPos.x < 1)
  618. this.bestPos = this.rootPos;
  619. let wObj = this.wBall.gameObj;
  620. let destx = wObj.x;
  621. let desty = wObj.y;
  622. let dx = Math.abs(destx - this.bestPos.x);
  623. let dy = Math.abs(desty - this.bestPos.y);
  624. let mx = 3 + dx * 0.03;
  625. let my = 3 + dy * 0.03;
  626. if (dx < 4 && dy < 4) {
  627. let dt = Laya.timer.currTimer - this.rootMoveTime;
  628. dt = dt < 1000 ? 1000 - dt : 0;
  629. if (this.setMax > 0) {
  630. let min = this.setMax * 0.3 + this.aiLv * 5;
  631. this.setRate = xGame.tools.random(min, this.setMax);
  632. this.setState(AIState.reset, 0);
  633. }
  634. else {
  635. this.destRot = 0; //
  636. this.forceMax = this.bestForce;
  637. this.setKick(this.bestKick.x, this.bestKick.y);
  638. }
  639. if (dt > 100) {
  640. Laya.timer.clear(this, this.delayLine)
  641. Laya.timer.once(dt, this, this.delayLine);
  642. }
  643. else
  644. this.gameSrc.aiRootMove(false);
  645. }
  646. else {
  647. if (dx > 4)
  648. destx = wObj.x < this.bestPos.x ? wObj.x + mx : wObj.x - mx;
  649. if (dy > 4)
  650. desty = wObj.y < this.bestPos.y ? wObj.y + my : wObj.y - my;
  651. this.gameSrc.onRootMove({ x: destx, y: desty });
  652. }
  653. }
  654. resetRoot() {
  655. this.bestAngle = 180;
  656. this.bestForce = 0;
  657. this.setMax = this.posList.length;
  658. this.posIdx = xGame.tools.random(0, this.setMax);
  659. let min = this.setMax * 0.3 + this.aiLv * 5;
  660. this.setRate = xGame.tools.random(min, this.setMax);
  661. let max = 2000 - this.aiLv * 100;
  662. if (Math.random() > 0.8)
  663. max += xGame.tools.random(100, 1000);
  664. if (this.isGuide)
  665. this.setState(AIState.select, 500);
  666. else if (this.onAway(2))
  667. this.setState(AIState.reset, xGame.tools.random(400, max));
  668. else
  669. this.setState(AIState.none);
  670. if (xGame.common.gameMgr.finishOneShoot)
  671. this.chat();
  672. }
  673. //运行ai
  674. runAI() {
  675. if (this.state > AIState.idle)
  676. return;
  677. this.initBall();
  678. this.onGame(2500);
  679. if (Math.random() > 0.6)
  680. this.chat();
  681. }
  682. stopRun() {
  683. this.setState(AIState.idle);
  684. }
  685. onGame(base: number) {
  686. let max = base - this.aiLv * 100;
  687. if (Math.random() > 0.8)
  688. max += xGame.tools.random(100, 1000);
  689. if (this.onAway(2))
  690. this.setState(AIState.select, xGame.tools.random(500, max));
  691. else
  692. this.setState(AIState.none);
  693. }
  694. setState(sta, wt = 0) {
  695. if (this.state == AIState.none)
  696. return;
  697. this.state = sta;
  698. this.waitTime = wt;
  699. }
  700. onUpdate() {
  701. if (this.state == AIState.none)
  702. return;
  703. this.awayTime -= this.dtime;
  704. if (this.awayTime < 1) {
  705. this.awayTime = xGame.tools.random(6000, 10000);
  706. this.awayNum += (11 - this.aiLv);
  707. this.onAway(1);
  708. }
  709. if (this.waitTime >= 0) {
  710. this.waitTime -= this.dtime;
  711. return;
  712. }
  713. switch (this.state) {
  714. case AIState.idle:
  715. this.chat();
  716. break;
  717. case AIState.reset:
  718. this.reset();
  719. break;
  720. case AIState.move:
  721. this.move();
  722. break;
  723. case AIState.leave:
  724. this.state = AIState.none;
  725. xGame.common.gameMgr.aiGameEnd(true);
  726. break;
  727. case AIState.select:
  728. this.selectBall();
  729. break;
  730. case AIState.turn:
  731. this.turnPole();
  732. break;
  733. case AIState.shoot:
  734. this.shoot();
  735. break;
  736. }
  737. }
  738. onAway(type) {
  739. if (type == 1)//逃跑
  740. {
  741. let db = xGame.common.gameMgr.aiBallArr.length - xGame.common.gameMgr.selfBallArr.length;
  742. let num = this.awayNum + db * 20;
  743. if (db > 0 && num > 0 && xGame.tools.random(0, 10000) < num && this.state == AIState.idle) {
  744. xGame.common.gameMgr.aiLeave();
  745. this.setState(AIState.leave, 6666);
  746. }
  747. }
  748. else if (type == 2) //不打(超时)
  749. {
  750. let db = xGame.common.gameMgr.aiBallArr.length - xGame.common.gameMgr.selfBallArr.length;
  751. let num = this.awayNum + db * 30;
  752. if (xGame.tools.random(0, 10000) < num)
  753. return false
  754. }
  755. else if (type == 3) //拉杆延迟(可能超时)
  756. {
  757. let db = xGame.common.gameMgr.aiBallArr.length - xGame.common.gameMgr.selfBallArr.length;
  758. let num = this.awayNum + db * 10;
  759. if (xGame.tools.random(0, 4000) < num)
  760. return false
  761. }
  762. return true;
  763. }
  764. chat() {
  765. if (this.chatTime > 0) {
  766. this.chatTime -= this.dtime;
  767. return;
  768. }
  769. this.chatTime = xGame.tools.random(2000, 10000);
  770. let rand = xGame.tools.random(0, 100);
  771. if (rand < 15)
  772. this.talk();
  773. else if (rand < 35)
  774. this.emotion();
  775. }
  776. talk() {
  777. xGame.common.talkMgr.showAITalk(0);
  778. }
  779. emotion() {
  780. xGame.common.talkMgr.showAIEmotion(0);
  781. }
  782. resetAI() {
  783. this.stopRun();
  784. this.state = AIState.none;
  785. }
  786. }