PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/src/org/void1898/www/agilebuddy/material/UIModel.java

https://github.com/catrixs/AgileBuddy
Java | 576 lines | 429 code | 64 blank | 83 comment | 111 complexity | adb1700db96bf702a498505a0fd9f4d9 MD5 | raw file
  1. package org.void1898.www.agilebuddy.material;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. import java.util.Random;
  5. import com.feiyang.agilebuddy.client.transport.UdpClient;
  6. import com.feiyang.agilebuddy.common.MessageDefinition;
  7. import org.void1898.www.agilebuddy.data.Food;
  8. import org.void1898.www.agilebuddy.data.Footboard;
  9. import org.void1898.www.agilebuddy.data.Role;
  10. import org.void1898.www.agilebuddy.data.ScreenAttribute;
  11. /**
  12. * @author void1898@gmail.com
  13. * @version 1.2.3
  14. */
  15. public class UIModel {
  16. /**
  17. * 游戏属性常量
  18. */
  19. // 帧刷新间隔(单位微妙)
  20. public static final int GAME_ATTRIBUTE_FRAME_DELAY = 30;
  21. // 游戏活动对象Y方向的像素密度(将1个单位像素拆分为更小单元)
  22. public static final int GAME_ATTRIBUTE_PIXEL_DENSITY_Y = 10;
  23. // 游戏等级提升因数
  24. public static final int GAME_ATTRIBUTE_LEVEL_UP_FACTOR = 40;
  25. // 重力速度(即主角离开踏板后的y方向速度)
  26. public static final int GAME_ATTRIBUTE_GRAVITY_VELOCITY = 5 * GAME_ATTRIBUTE_PIXEL_DENSITY_Y;
  27. // 游戏状态
  28. public static final int GAME_STATUS_PAUSE = 0;
  29. public static final int GAME_STATUS_RUNNING = 1;
  30. public static final int GAME_STATUS_GAMEOVER = 2;
  31. // 游戏效果标识(用来控制不同音效和震动的标志)
  32. public static final int EFFECT_FLAG_NO_EFFECT = 0;
  33. public static final int EFFECT_FLAG_NORMAL = 1;
  34. public static final int EFFECT_FLAG_UNSTABLE = 2;
  35. public static final int EFFECT_FLAG_SPRING = 3;
  36. public static final int EFFECT_FLAG_SPIKED = 4;
  37. public static final int EFFECT_FLAG_MOVING = 5;
  38. public static final int EFFECT_FLAG_TOOLS = 6;
  39. /**
  40. * 主角属性常量
  41. */
  42. // 主角的长度和宽度
  43. public static final int ROLE_ATTRIBUTE_WIDTH = 32;
  44. public static final int ROLE_ATTRIBUTE_HEITH = 48;
  45. // 主角帧刷新间隔
  46. public static final int ROLE_ATTRIBUTE_FRAME_DELAY = 2;
  47. // 主角最大生命值
  48. public static final int ROLE_ATTRIBUTE_HP_MAX = 12;
  49. // 主角状态
  50. public static final int ROLE_STATUS_ON_FOOTBOARD = 0;
  51. public static final int ROLE_STATUS_ON_FOOTBOARD_LEFT = 1;
  52. public static final int ROLE_STATUS_ON_FOOTBOARD_RIGHT = 2;
  53. public static final int ROLE_STATUS_FREEFALL = 3;
  54. public static final int ROLE_STATUS_FREEFALL_LEFT = 4;
  55. public static final int ROLE_STATUS_FREEFALL_RIGHT = 5;
  56. // 主角帧
  57. public static final int ROLE_SHARP_STANDING = 0;
  58. public static final int ROLE_SHARP_FREEFALL_NO1 = 1;
  59. public static final int ROLE_SHARP_FREEFALL_NO2 = 2;
  60. public static final int ROLE_SHARP_FREEFALL_NO3 = 3;
  61. public static final int ROLE_SHARP_FREEFALL_NO4 = 4;
  62. public static final int ROLE_SHARP_MOVE_LEFT_NO1 = 5;
  63. public static final int ROLE_SHARP_MOVE_LEFT_NO2 = 6;
  64. public static final int ROLE_SHARP_MOVE_LEFT_NO3 = 7;
  65. public static final int ROLE_SHARP_MOVE_LEFT_NO4 = 8;
  66. public static final int ROLE_SHARP_MOVE_RIGHT_NO1 = 9;
  67. public static final int ROLE_SHARP_MOVE_RIGHT_NO2 = 10;
  68. public static final int ROLE_SHARP_MOVE_RIGHT_NO3 = 11;
  69. public static final int ROLE_SHARP_MOVE_RIGHT_NO4 = 12;
  70. /**
  71. * 道具属性常量
  72. */
  73. // 加分物品在屏幕上的滞留时间
  74. public static final int FOOD_ATTRIBUTE_DELAY_TIME = 450;
  75. // 加分物品的大小
  76. public static final int FOOD_ATTRIBUTE_IMAGE_SIZE = 24;
  77. // 踏板的长宽
  78. public static final int BORDER_ATTRIBUTE_IMAGE_HEITH = 20;
  79. public static final int BORDER_ATTRIBUTE_IMAGE_WIDTH = 100;
  80. // 踏板偏向速度
  81. public static final int BOARD_ATTRIBUTE_LEFT_VELOCITY = -4;
  82. public static final int BOARD_ATTRIBUTE_RIGHT_VELOCITY = 4;
  83. // 不稳定踏板滞留因数(可滞留时间=滞留因数*帧刷新间隔)
  84. public static final int BOARD_ATTRIBUTE_UNSTABLE_DELAY_FACTOR = 10;
  85. // 踏板类别
  86. public static final int FOOTBOARD_TYPE_NORMAL = 0;
  87. public static final int FOOTBOARD_TYPE_UNSTABLE = 1;
  88. public static final int FOOTBOARD_TYPE_SPRING = 2;
  89. public static final int FOOTBOARD_TYPE_SPIKED = 3;
  90. public static final int FOOTBOARD_TYPE_MOVING_LEFT = 4;
  91. public static final int FOOTBOARD_TYPE_MOVING_RIGHT = 5;
  92. // 加分物品类别
  93. public static final int FOOD_NONE = 0;
  94. public static final int FOOD_8 = 4;
  95. public static final int FOOD_7 = 6;
  96. public static final int FOOD_6 = 8;
  97. public static final int FOOD_5 = 10;
  98. public static final int FOOD_4 = 12;
  99. public static final int FOOD_3 = 14;
  100. public static final int FOOD_2 = 16;
  101. public static final int FOOD_1 = 20;
  102. /**
  103. * 游戏属性
  104. */
  105. // 游戏界面属性
  106. private ScreenAttribute mScreenAttribute;
  107. // 游戏状态
  108. public int mGameStatus = GAME_STATUS_RUNNING;
  109. // 游戏得分
  110. private int mScore = 0;
  111. // 当前难度等级
  112. private int mLevel = 1;
  113. // 生命值
  114. private int mHP = ROLE_ATTRIBUTE_HP_MAX;
  115. // 游戏等级提升计算器(等级计算器值等于等级提升因数时游戏等级提升1级,等级计算器重置为零)
  116. private int mLevelUpCounter = 0;
  117. // 随机数生成器
  118. private Random mRan = new Random(MessageDefinition.HOST.hashCode());
  119. // 游戏效果标志(用于处理主角动作效果,比如:震动,音效)
  120. private int mEffectFlag = EFFECT_FLAG_NO_EFFECT;
  121. /**
  122. * 游戏主角属性
  123. */
  124. // 游戏主角
  125. public Role mRole;
  126. // 主角X方向移动速度
  127. public int mRoleVelocityX;
  128. // 主角Y方向移动速度
  129. public int mRoleVelocityY;
  130. // 附加速度(用于控制速度,在选项面板里设定)
  131. public int mAddVelocity;
  132. /**
  133. * 道具属性
  134. */
  135. // 楼梯间隔距离因数(间隔距离(px)=因数/Y方向像素因数)
  136. public int mFootboardSpaceFactor = 120 * GAME_ATTRIBUTE_PIXEL_DENSITY_Y;
  137. // 移动间隔计算器
  138. public int mFootboardSpaceCounter = 0;
  139. // 踏板移动速度
  140. public int mFootboartVelocity = -3 * GAME_ATTRIBUTE_PIXEL_DENSITY_Y;
  141. // 踏板列表
  142. public LinkedList<Footboard> mFootboardList;
  143. public Food mCurFood;
  144. public UIModel(ScreenAttribute screenAttribute, int addVelocity) {
  145. mScreenAttribute = screenAttribute;
  146. mAddVelocity = addVelocity;
  147. mRole = new Role((screenAttribute.maxX - ROLE_ATTRIBUTE_WIDTH) / 2,
  148. screenAttribute.maxY * 3 / 4, ROLE_ATTRIBUTE_WIDTH,
  149. ROLE_ATTRIBUTE_HEITH, ROLE_ATTRIBUTE_FRAME_DELAY);
  150. mRoleVelocityY = GAME_ATTRIBUTE_GRAVITY_VELOCITY;
  151. mFootboardList = new LinkedList<Footboard>();
  152. mFootboardList.add(new Footboard(
  153. (screenAttribute.maxX - BORDER_ATTRIBUTE_IMAGE_WIDTH) / 2,
  154. screenAttribute.maxY, BORDER_ATTRIBUTE_IMAGE_WIDTH,
  155. BORDER_ATTRIBUTE_IMAGE_HEITH, FOOTBOARD_TYPE_NORMAL, 1, 1));
  156. mCurFood = new Food(FOOD_NONE, 0, 0, 0, FOOD_ATTRIBUTE_IMAGE_SIZE);
  157. }
  158. /**
  159. * 更新UI模型
  160. */
  161. public void updateUIModel() {
  162. for (Footboard footboard : mFootboardList) {
  163. footboard.addY(mFootboartVelocity);
  164. }
  165. mRole.addX(mRoleVelocityX);
  166. mRole.addY(mRoleVelocityY);
  167. handleBorder();
  168. handleRoleAction();
  169. handleFood();
  170. mFootboardSpaceCounter = mFootboardSpaceCounter - mFootboartVelocity;
  171. if (mFootboardSpaceCounter >= mFootboardSpaceFactor) {
  172. mFootboardSpaceCounter -= mFootboardSpaceFactor;
  173. generateFootboard();
  174. generateFood();
  175. mLevelUpCounter += 1;
  176. if (mLevelUpCounter == GAME_ATTRIBUTE_LEVEL_UP_FACTOR) {
  177. mLevelUpCounter = 0;
  178. increaseLevel();
  179. }
  180. }
  181. }
  182. /**
  183. * 随机生成踏板
  184. */
  185. private void generateFootboard() {
  186. int frameAmount = 1;
  187. int frameDelay = 1;
  188. int frameType = FOOTBOARD_TYPE_NORMAL;
  189. switch (mRan.nextInt(20)) {
  190. case 0:
  191. case 1:
  192. case 2:
  193. frameType = FOOTBOARD_TYPE_UNSTABLE;
  194. break;
  195. case 3:
  196. case 4:
  197. case 5:
  198. frameType = FOOTBOARD_TYPE_SPRING;
  199. break;
  200. case 6:
  201. case 7:
  202. case 8:
  203. frameType = FOOTBOARD_TYPE_SPIKED;
  204. break;
  205. case 9:
  206. case 10:
  207. frameType = FOOTBOARD_TYPE_MOVING_LEFT;
  208. frameAmount = 2;
  209. frameDelay = 15;
  210. break;
  211. case 11:
  212. case 12:
  213. frameType = FOOTBOARD_TYPE_MOVING_RIGHT;
  214. frameAmount = 2;
  215. frameDelay = 15;
  216. break;
  217. default:
  218. frameType = FOOTBOARD_TYPE_NORMAL;
  219. }
  220. mFootboardList.add(new Footboard(mRan.nextInt(mScreenAttribute.maxX
  221. - BORDER_ATTRIBUTE_IMAGE_WIDTH), mScreenAttribute.maxY
  222. + ROLE_ATTRIBUTE_HEITH, BORDER_ATTRIBUTE_IMAGE_WIDTH,
  223. BORDER_ATTRIBUTE_IMAGE_HEITH, frameType, frameAmount,
  224. frameDelay));
  225. }
  226. /**
  227. * 随机生成加分物品
  228. */
  229. private void generateFood() {
  230. if (mCurFood.mTimeCounter > 0) {
  231. return;
  232. }
  233. switch (mRan.nextInt(25)) {
  234. case 0:
  235. mCurFood.mFoodReward = FOOD_1;
  236. break;
  237. case 1:
  238. mCurFood.mFoodReward = FOOD_2;
  239. break;
  240. case 2:
  241. case 3:
  242. case 4:
  243. mCurFood.mFoodReward = FOOD_3;
  244. break;
  245. case 5:
  246. case 6:
  247. case 7:
  248. mCurFood.mFoodReward = FOOD_4;
  249. break;
  250. case 8:
  251. case 9:
  252. case 10:
  253. mCurFood.mFoodReward = FOOD_5;
  254. break;
  255. case 11:
  256. case 12:
  257. case 13:
  258. mCurFood.mFoodReward = FOOD_6;
  259. break;
  260. case 14:
  261. case 15:
  262. case 16:
  263. case 17:
  264. mCurFood.mFoodReward = FOOD_7;
  265. break;
  266. case 18:
  267. case 19:
  268. case 20:
  269. case 21:
  270. mCurFood.mFoodReward = FOOD_8;
  271. break;
  272. default:
  273. mCurFood.mFoodReward = FOOD_NONE;
  274. return;
  275. }
  276. mCurFood.mMinX = mRan
  277. .nextInt((mScreenAttribute.maxX - FOOD_ATTRIBUTE_IMAGE_SIZE));
  278. mCurFood.mMinY = mRan
  279. .nextInt((mScreenAttribute.maxY - FOOD_ATTRIBUTE_IMAGE_SIZE));
  280. mCurFood.mMaxX = mCurFood.mMinX + FOOD_ATTRIBUTE_IMAGE_SIZE;
  281. mCurFood.mMaxY = mCurFood.mMinY + FOOD_ATTRIBUTE_IMAGE_SIZE;
  282. mCurFood.mTimeCounter = FOOD_ATTRIBUTE_DELAY_TIME;
  283. }
  284. /**
  285. * 处理主角移动
  286. *
  287. * @param angleValue
  288. */
  289. public void handleMoving(float angleValue) {
  290. if (angleValue < -5) {
  291. mRoleVelocityX = 10 + mAddVelocity;
  292. } else if (angleValue >= -5 && angleValue < -4) {
  293. mRoleVelocityX = 8 + mAddVelocity;
  294. } else if (angleValue >= -4 && angleValue < -3) {
  295. mRoleVelocityX = 6 + mAddVelocity;
  296. } else if (angleValue >= -3 && angleValue < -2) {
  297. mRoleVelocityX = 5 + mAddVelocity;
  298. } else if (angleValue >= -2 && angleValue < -1.5) {
  299. mRoleVelocityX = 4 + mAddVelocity;
  300. } else if (angleValue >= -1.5 && angleValue < 1.5) {
  301. mRoleVelocityX = 0;
  302. } else if (angleValue >= 1.5 && angleValue < 2) {
  303. mRoleVelocityX = -4 - mAddVelocity;
  304. } else if (angleValue >= 2 && angleValue < 3) {
  305. mRoleVelocityX = -5 - mAddVelocity;
  306. } else if (angleValue >= 3 && angleValue < 4) {
  307. mRoleVelocityX = -6 - mAddVelocity;
  308. } else if (angleValue >= 4 && angleValue < 5) {
  309. mRoleVelocityX = -8 - mAddVelocity;
  310. } else if (angleValue > 5) {
  311. mRoleVelocityX = -10 - mAddVelocity;
  312. }
  313. }
  314. /**
  315. * 难度提升
  316. */
  317. private void increaseLevel() {
  318. mLevel++;
  319. if (mLevel < 18 || mLevel % 20 == 0) {
  320. mFootboartVelocity -= 2;
  321. int roleStatus = mRole.getRoleStatus();
  322. if (roleStatus == ROLE_STATUS_ON_FOOTBOARD
  323. || roleStatus == ROLE_STATUS_ON_FOOTBOARD_RIGHT
  324. || roleStatus == ROLE_STATUS_ON_FOOTBOARD_LEFT) {
  325. mRoleVelocityY = mFootboartVelocity;
  326. }
  327. }
  328. }
  329. /**
  330. * 处理边界
  331. */
  332. private void handleBorder() {
  333. if (mFootboardList.size() > 0
  334. && mFootboardList.getFirst().getMaxY() <= mScreenAttribute.minY) {
  335. mFootboardList.remove();
  336. }
  337. if (mRole.getMinY() <= mScreenAttribute.minY) {
  338. mHP -= 3;
  339. if (mHP <= 0) {
  340. mGameStatus = GAME_STATUS_GAMEOVER;
  341. } else if (mRole.getRoleStatus() == ROLE_STATUS_ON_FOOTBOARD
  342. || mRole.getRoleStatus() == ROLE_STATUS_ON_FOOTBOARD_LEFT
  343. || mRole.getRoleStatus() == ROLE_STATUS_ON_FOOTBOARD_RIGHT) {
  344. mRole.addY(BORDER_ATTRIBUTE_IMAGE_HEITH
  345. * GAME_ATTRIBUTE_PIXEL_DENSITY_Y);
  346. }
  347. mRoleVelocityY = GAME_ATTRIBUTE_GRAVITY_VELOCITY;
  348. mEffectFlag = EFFECT_FLAG_SPIKED;
  349. return;
  350. }
  351. if (mRole.getMinY() > mScreenAttribute.maxY) {
  352. mGameStatus = GAME_STATUS_GAMEOVER;
  353. return;
  354. }
  355. if (mRole.getMinX() < mScreenAttribute.minX) {
  356. mRoleVelocityX = 0;
  357. mRole.setX(0);
  358. return;
  359. }
  360. if (mRole.getMaxX() > mScreenAttribute.maxX) {
  361. mRoleVelocityX = 0;
  362. mRole.setX(mScreenAttribute.maxX - ROLE_ATTRIBUTE_WIDTH);
  363. return;
  364. }
  365. }
  366. /**
  367. * 处理主角在踏板上的活动
  368. */
  369. private void handleRoleAction() {
  370. Role role = mRole;
  371. for (Footboard footboard : mFootboardList) {
  372. if ((role.getMaxY() >= footboard.getMinY() && role.getMaxY() < footboard
  373. .getMaxY())
  374. && (role.getMaxX() > footboard.getMinX() && role.getMinX() < footboard
  375. .getMaxX())) {
  376. if (role.getRoleStatus() == ROLE_STATUS_ON_FOOTBOARD
  377. || role.getRoleStatus() == ROLE_STATUS_ON_FOOTBOARD_RIGHT
  378. || role.getRoleStatus() == ROLE_STATUS_ON_FOOTBOARD_LEFT) {
  379. if (footboard.getType() == FOOTBOARD_TYPE_SPRING) {
  380. mRoleVelocityY = mFootboartVelocity
  381. - GAME_ATTRIBUTE_GRAVITY_VELOCITY;
  382. role.addY(-1 * GAME_ATTRIBUTE_PIXEL_DENSITY_Y);
  383. updateRoleStatus(ROLE_STATUS_FREEFALL);
  384. return;
  385. }
  386. if (footboard.getType() == FOOTBOARD_TYPE_MOVING_LEFT) {
  387. role.addX(BOARD_ATTRIBUTE_LEFT_VELOCITY);
  388. } else if (footboard.getType() == FOOTBOARD_TYPE_MOVING_RIGHT) {
  389. role.addX(BOARD_ATTRIBUTE_RIGHT_VELOCITY);
  390. } else if (footboard.getType() == FOOTBOARD_TYPE_UNSTABLE
  391. && footboard.isBoardBreak()) {
  392. mFootboardList.remove(footboard);
  393. }
  394. updateRoleStatus(ROLE_STATUS_ON_FOOTBOARD);
  395. } else {
  396. // 主角第一次触板
  397. mScore += mLevel;
  398. mRoleVelocityY = mFootboartVelocity;
  399. role.setVirtualY(footboard.getVirtualY()
  400. - ROLE_ATTRIBUTE_HEITH
  401. * GAME_ATTRIBUTE_PIXEL_DENSITY_Y);
  402. if (footboard.getType() == FOOTBOARD_TYPE_SPIKED) {
  403. mHP -= 3;
  404. } else if (mHP < ROLE_ATTRIBUTE_HP_MAX) {
  405. mHP += 1;
  406. }
  407. if (mHP <= 0) {
  408. mGameStatus = GAME_STATUS_GAMEOVER;
  409. }
  410. updateRoleStatus(ROLE_STATUS_ON_FOOTBOARD);
  411. switch (footboard.getType()) {
  412. case FOOTBOARD_TYPE_UNSTABLE:
  413. mEffectFlag = EFFECT_FLAG_UNSTABLE;
  414. break;
  415. case FOOTBOARD_TYPE_SPRING:
  416. mEffectFlag = EFFECT_FLAG_SPRING;
  417. break;
  418. case FOOTBOARD_TYPE_SPIKED:
  419. mEffectFlag = EFFECT_FLAG_SPIKED;
  420. break;
  421. case FOOTBOARD_TYPE_MOVING_LEFT:
  422. case FOOTBOARD_TYPE_MOVING_RIGHT:
  423. mEffectFlag = EFFECT_FLAG_MOVING;
  424. break;
  425. default:
  426. mEffectFlag = EFFECT_FLAG_NORMAL;
  427. }
  428. }
  429. return;
  430. }
  431. }
  432. if (mRoleVelocityY < mFootboartVelocity) {
  433. mRoleVelocityY += 3;
  434. } else {
  435. mRoleVelocityY = GAME_ATTRIBUTE_GRAVITY_VELOCITY;
  436. }
  437. updateRoleStatus(ROLE_STATUS_FREEFALL);
  438. }
  439. private void handleFood() {
  440. Food food = mCurFood;
  441. food.mTimeCounter--;
  442. if (food.mFoodReward != FOOD_NONE && food.mTimeCounter > 0) {
  443. if ((mRole.getMaxX() > food.mMinX && mRole.getMinX() < food.mMaxX)
  444. && ((mRole.getMaxY() >= food.mMinY && mRole.getMaxY() < food.mMaxY) || (mRole
  445. .getMinY() > food.mMinY && mRole.getMinY() <= food.mMaxY))) {
  446. mEffectFlag = EFFECT_FLAG_TOOLS;
  447. mScore += food.mFoodReward;
  448. food.mFoodReward = FOOD_NONE;
  449. }
  450. }
  451. }
  452. private void updateRoleStatus(int status) {
  453. if (status == ROLE_STATUS_FREEFALL) {
  454. if (mRoleVelocityX > 0) {
  455. mRole.setRoleStatus(ROLE_STATUS_FREEFALL_RIGHT);
  456. } else if (mRoleVelocityX < 0) {
  457. mRole.setRoleStatus(ROLE_STATUS_FREEFALL_LEFT);
  458. } else {
  459. mRole.setRoleStatus(ROLE_STATUS_FREEFALL);
  460. }
  461. } else {
  462. if (mRoleVelocityX > 0) {
  463. mRole.setRoleStatus(ROLE_STATUS_ON_FOOTBOARD_RIGHT);
  464. } else if (mRoleVelocityX < 0) {
  465. mRole.setRoleStatus(ROLE_STATUS_ON_FOOTBOARD_LEFT);
  466. } else {
  467. mRole.setRoleStatus(ROLE_STATUS_ON_FOOTBOARD);
  468. }
  469. }
  470. }
  471. /**
  472. * 清除操作
  473. */
  474. public void destroy() {
  475. mScreenAttribute = null;
  476. mRole = null;
  477. mRan = null;
  478. mFootboardList.clear();
  479. mFootboardList = null;
  480. }
  481. public Role getRoleUIObject() {
  482. return mRole;
  483. }
  484. public List<Footboard> getFootboardUIObjects() {
  485. return mFootboardList;
  486. }
  487. public Food getFood() {
  488. return mCurFood;
  489. }
  490. public int getEffectFlag() {
  491. try {
  492. return mEffectFlag;
  493. } finally {
  494. mEffectFlag = EFFECT_FLAG_NO_EFFECT;
  495. }
  496. }
  497. public String getLevel() {
  498. return "LV: " + mLevel;
  499. }
  500. public String getScoreStr() {
  501. return "SC: " + mScore;
  502. }
  503. public int getScore() {
  504. return mScore;
  505. }
  506. public float getHp() {
  507. return (float) mHP / ROLE_ATTRIBUTE_HP_MAX;
  508. }
  509. public void setSeed(long seed) {
  510. mRan.setSeed(seed);
  511. }
  512. }