Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

696 lines
15 KiB

  1. /*
  2. SNAKE
  3. by matty riek.
  4. */
  5. global showMemoryUsage = false;
  6. CURSOR(0,0);
  7. global g_sx = 80;
  8. global g_sy = 24;
  9. global g_border =
  10. "\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205"
  11. "\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205"
  12. "\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205";
  13. global LocFromXY = function(a_x, a_y) { return a_y * g_sx + a_x; };
  14. global XFromLoc = function(a_loc) { return a_loc % g_sx; };
  15. global YFromLoc = function(a_loc) { return a_loc / g_sx; };
  16. global background = CA.B_BLUE;
  17. global fade0 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
  18. CA.F_GREEN | CA.F_INTENSITY | CA.F_INTENSITY | background,
  19. CA.F_BLUE | background,
  20. CA.F_GREEN | CA.F_INTENSITY | background,
  21. CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
  22. CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
  23. CA.F_BLUE | CA.F_INTENSITY | background,
  24. CA.F_BLUE | CA.F_INTENSITY | background,
  25. CA.F_RED|CA.F_BLUE|CA.F_GREEN | background,
  26. CA.F_BLUE | background,
  27. CA.F_BLUE | CA.F_INTENSITY | background,
  28. CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background );
  29. global fade1 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
  30. CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
  31. CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
  32. CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
  33. CA.F_RED | CA.F_INTENSITY | background,
  34. CA.F_RED | background,
  35. CA.F_RED | CA.F_INTENSITY | background,
  36. CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background );
  37. global fadeRed = table( CA.F_RED|CA.F_INTENSITY | background , CA.F_RED | background);
  38. ///////////////////////////////////////////////////////////////////////////////////////////////////
  39. //
  40. // CreateGemType
  41. //
  42. global CreateGemType = function(a_char, a_time, a_score, a_chance, a_length, a_cycle, a_pickup, a_mover)
  43. {
  44. gem = table(m_char = a_char, m_time = a_time * 1000, m_score = a_score, m_chance = a_chance, m_length = a_length, m_cycle = a_cycle, m_mover = a_mover);
  45. gem.Pickup = function()
  46. {
  47. };
  48. if(a_pickup)
  49. {
  50. gem.Pickup = a_pickup;
  51. }
  52. gem.i = 0;
  53. gem.Draw = function(x, y)
  54. {
  55. .i = .i + 1;
  56. if(.i >= tableCount(.m_cycle))
  57. {
  58. .i = 0;
  59. }
  60. CATTRIB(.m_cycle[.i]);
  61. XYTEXT(x, y, .m_char);
  62. CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
  63. };
  64. return gem;
  65. };
  66. ///////////////////////////////////////////////////////////////////////////////////////////////////
  67. //
  68. // DrawScreen
  69. //
  70. global DrawScreen = function()
  71. {
  72. //
  73. CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
  74. // clear screen
  75. CLS();
  76. // draw border
  77. XYTEXT(1, 0, g_border);
  78. XYTEXT(1, g_sy - 1, g_border);
  79. ey = g_sy - 1;
  80. for(i = 1; i < ey; i = i + 1)
  81. {
  82. XYTEXT(0, i, "\186");
  83. XYTEXT(g_sx - 1, i, "\186");
  84. }
  85. XYTEXT(0, 0, "\201");
  86. XYTEXT(g_sx - 1, 0, "\187");
  87. XYTEXT(0, ey, "\200");
  88. XYTEXT(g_sx - 1, ey, "\188");
  89. };
  90. ///////////////////////////////////////////////////////////////////////////////////////////////////
  91. //
  92. // EmitGems
  93. //
  94. EmitGems = function(a_game)
  95. {
  96. // add a gem
  97. for(;;)
  98. {
  99. // choose a random sleep time
  100. sleep( randfloat(
  101. a_game.m_levels[a_game.m_level][1] / a_game.m_gameSpeed,
  102. a_game.m_levels[a_game.m_level][2] / a_game.m_gameSpeed)
  103. );
  104. // pick a random location
  105. x = randint(1, g_sx - 2);
  106. y = randint(1, g_sy - 2);
  107. loc = LocFromXY(x, y);
  108. // test if the location is on a existing gem or snake
  109. if(a_game.m_gems[loc])
  110. {
  111. continue;
  112. }
  113. onsnake = false;
  114. foreach(snake in a_game.m_snakes)
  115. {
  116. if(snake.IsAt(loc))
  117. {
  118. onsnake = true;
  119. break;
  120. }
  121. }
  122. if(onsnake) { continue; }
  123. // choose a gem
  124. found = false;
  125. while(!found)
  126. {
  127. foreach(gem in a_game.m_gemTypes)
  128. {
  129. if(randint(0, 100) < gem.m_chance)
  130. {
  131. found = true;
  132. break;
  133. }
  134. }
  135. }
  136. // add the gem
  137. gemInstance = table(m_gem = gem, m_expire = sysTime() + (gem.m_time / a_game.m_gameSpeed));
  138. a_game.m_gems[loc] = gemInstance;
  139. if(gem.m_mover)
  140. {
  141. gemInstance.m_dirX = randint(-1, 2);
  142. gemInstance.m_dirY = randint(-1, 2);
  143. }
  144. // draw the gem
  145. gem.Draw(x, y);
  146. }
  147. };
  148. ///////////////////////////////////////////////////////////////////////////////////////////////////
  149. //
  150. // ReclaimGems
  151. //
  152. ReclaimGems = function(a_game)
  153. {
  154. // add a gem
  155. for(;;)
  156. {
  157. time = sysTime();
  158. foreach(key and gem in a_game.m_gems)
  159. {
  160. if(time > gem.m_expire)
  161. {
  162. // remove from screen
  163. XYTEXT(XFromLoc(key), YFromLoc(key), " ");
  164. a_game.m_gems[key] = null;
  165. b = true;
  166. break;
  167. }
  168. }
  169. yield();
  170. }
  171. };
  172. ///////////////////////////////////////////////////////////////////////////////////////////////////
  173. //
  174. // DrawGems
  175. //
  176. DrawGems = function(a_game)
  177. {
  178. for(;;)
  179. {
  180. foreach(key and gem in a_game.m_gems)
  181. {
  182. x = XFromLoc(key);
  183. y = YFromLoc(key);
  184. gem.m_gem.Draw(x, y);
  185. }
  186. sleep(1 / 30.);
  187. }
  188. };
  189. ///////////////////////////////////////////////////////////////////////////////////////////////////
  190. //
  191. // Move mover gems
  192. //
  193. MoveMovers = function(a_game)
  194. {
  195. for(;;)
  196. {
  197. movers = table();
  198. foreach(key and gem in a_game.m_gems)
  199. {
  200. if(gem.m_gem.m_mover)
  201. {
  202. x = XFromLoc(key);
  203. y = YFromLoc(key);
  204. XYTEXT(x, y, " ");
  205. x = x + gem.m_dirX;
  206. y = y + gem.m_dirY;
  207. if(x <= 1 or x >= g_sx - 2) { gem.m_dirX = -gem.m_dirX; }
  208. if(y <= 1 or y >= g_sy - 2) { gem.m_dirY = -gem.m_dirY; }
  209. gem.m_newLoc = LocFromXY(x, y);
  210. movers[key] = gem;
  211. }
  212. }
  213. foreach(key and gem in movers)
  214. {
  215. a_game.m_gems[key] = null;
  216. a_game.m_gems[gem.m_newLoc] = gem;
  217. }
  218. sleep(1 / 4.);
  219. }
  220. };
  221. ///////////////////////////////////////////////////////////////////////////////////////////////////
  222. //
  223. // Snake
  224. //
  225. Snake = function(a_id, a_name, a_score, a_scoreX, a_scoreY, a_headChar, a_tailChar, a_startX, a_startY, a_startDir, a_keys)
  226. {
  227. snake = table(
  228. m_id = a_id,
  229. m_grow = 2,
  230. m_credits = 3,
  231. m_startX = a_startX,
  232. m_startY = a_startY,
  233. m_startDir = a_startDir,
  234. m_name = a_name,
  235. m_score = a_score,
  236. m_scoreX = a_scoreX,
  237. m_scoreY = a_scoreY,
  238. m_body = array(50),
  239. m_length = 0,
  240. m_dir = a_startDir, // 0 right, 1 up, 2 left, 3 down
  241. m_head = table(
  242. m_headChar = a_headChar,
  243. m_tailChar = a_tailChar,
  244. m_x = a_startX,
  245. m_y = a_startY
  246. ),
  247. m_keys = a_keys
  248. );
  249. ///////////////////////////////////////////////////////////////////////////////////////////////////
  250. //
  251. // Die
  252. //
  253. snake.Die = function() // return true if snake has no credits
  254. {
  255. // flash a death thinggy
  256. i = 10;
  257. while(i)
  258. {
  259. if(i & 1) {
  260. XYTEXT(.m_scoreX, .m_scoreY, format(" *** FATALITY *** ", .m_name));
  261. }
  262. else {
  263. XYTEXT(.m_scoreX, .m_scoreY, format(" ", .m_name));
  264. }
  265. sleep(0.2f);
  266. i = i - 1;
  267. }
  268. .UpdateScore();
  269. // clear the snake
  270. for(i = 0; i < .m_length; i = i + 1)
  271. {
  272. loc = .m_body[i];
  273. x = XFromLoc(loc);
  274. y = YFromLoc(loc);
  275. XYTEXT(x, y, " ");
  276. }
  277. XYTEXT(.m_head.m_x, .m_head.m_y, " ");
  278. // update the credits
  279. .m_credits = .m_credits - 1;
  280. if(.m_credits)
  281. {
  282. .m_head.m_x = .m_startX;
  283. .m_head.m_y = .m_startY;
  284. .m_dir = .m_startDir;
  285. .m_grow = 2;
  286. .m_length = 0;
  287. return false;
  288. }
  289. return true;
  290. };
  291. ///////////////////////////////////////////////////////////////////////////////////////////////////
  292. //
  293. // UpdateScore
  294. //
  295. snake.UpdateScore = function()
  296. {
  297. XYTEXT(.m_scoreX, .m_scoreY, format(" %d UP %s SCORE %d ", .m_credits, .m_name, .m_score));
  298. };
  299. ///////////////////////////////////////////////////////////////////////////////////////////////////
  300. //
  301. // Draw
  302. //
  303. snake.Draw = function(a_oldX, a_oldY)
  304. {
  305. // clear the tail.
  306. if(.m_length > 0 and .grow == null)
  307. {
  308. loc = .m_body[.m_length - 1];
  309. y = YFromLoc(loc);
  310. x = XFromLoc(loc);
  311. XYTEXT(x, y, " ");
  312. }
  313. // grow the snake
  314. if(.grow)
  315. {
  316. .m_length = .m_length + 1;
  317. if(.m_length >= .m_body.Size())
  318. {
  319. .m_body.Resize(.m_body.Size() * 2);
  320. }
  321. .grow = false;
  322. }
  323. // move the body along.
  324. for(i = .m_length - 1; i > 0; i = i - 1)
  325. {
  326. .m_body[i] = .m_body[i - 1];
  327. }
  328. // put the new tail piece in
  329. if(.m_length)
  330. {
  331. .m_body[0] = LocFromXY(a_oldX, a_oldY);
  332. XYTEXT(a_oldX, a_oldY, .m_head.m_tailChar);
  333. }
  334. else
  335. {
  336. XYTEXT(a_oldX, a_oldY, " ");
  337. }
  338. // draw the new head
  339. XYTEXT(.m_head.m_x, .m_head.m_y, .m_head.m_headChar);
  340. };
  341. ///////////////////////////////////////////////////////////////////////////////////////////////////
  342. //
  343. // IsAt
  344. //
  345. snake.IsAt = function(a_loc)
  346. {
  347. for(i = 0; i < .m_length; i = i + 1)
  348. {
  349. if(a_loc == .m_body[i])
  350. {
  351. return true;
  352. }
  353. }
  354. if(a_loc == LocFromXY(.m_head.m_x, .m_head.m_y))
  355. {
  356. return true;
  357. }
  358. return false;
  359. };
  360. ///////////////////////////////////////////////////////////////////////////////////////////////////
  361. //
  362. // Update
  363. //
  364. snake.Update = function(a_game)
  365. {
  366. member UpdateScore;
  367. UpdateScore();
  368. for(;;)
  369. {
  370. wait = 1.0f / (a_game.m_snakeMoveRate * a_game.m_gameSpeed);
  371. sleep(wait);
  372. x = .m_head.m_x;
  373. y = .m_head.m_y;
  374. if(.m_dir == 0) { x = x + 1; }
  375. else if(.m_dir == 1) { y = y - 1; }
  376. else if(.m_dir == 2) { x = x - 1; }
  377. else if(.m_dir == 3) { y = y + 1; }
  378. // did we collect a gem?
  379. loc = LocFromXY(x, y);
  380. gem = a_game.m_gems[loc];
  381. if(gem)
  382. {
  383. // update score
  384. .m_score = .m_score + gem.m_gem.m_score;
  385. .m_grow = gem.m_gem.m_length;
  386. pickup = gem.m_gem.Pickup;
  387. this:pickup();
  388. UpdateScore();
  389. // remove the gem
  390. a_game.m_gems[loc] = null;
  391. }
  392. // did we run into a wall??
  393. dead = false;
  394. if(x <= 0 or x >= g_sx - 1 or y <= 0 or y >= g_sy - 1)
  395. {
  396. dead = true;
  397. }
  398. // did we run into ourselves?
  399. if(!dead and .IsAt(loc))
  400. {
  401. dead = true;
  402. }
  403. // update position
  404. oldX = .m_head.m_x;
  405. oldY = .m_head.m_y;
  406. .m_head.m_x = x;
  407. .m_head.m_y = y;
  408. // did we run into another snake
  409. if(!dead)
  410. {
  411. foreach(snake in a_game.m_snakes)
  412. {
  413. if(snake != this and snake.IsAt(loc))
  414. {
  415. dead = true;
  416. break;
  417. }
  418. }
  419. }
  420. // did we die?
  421. if(dead)
  422. {
  423. .m_head.m_x = oldX;
  424. .m_head.m_y = oldY;
  425. die = .Die();
  426. .UpdateScore();
  427. if(die)
  428. {
  429. a_game.m_snakes[.m_id] = null;
  430. threadKill(.kht);
  431. exit();
  432. }
  433. }
  434. .Draw(oldX, oldY);
  435. if(.m_grow)
  436. {
  437. .grow = true;
  438. .m_grow = .m_grow - 1;
  439. }
  440. }
  441. };
  442. ///////////////////////////////////////////////////////////////////////////////////////////////////
  443. //
  444. // KeyHandler
  445. //
  446. snake.KeyHandler = function(a_game)
  447. {
  448. wait = 1.0f / a_game.m_keyUpdateRate;
  449. for(;;)
  450. {
  451. for(i = 0; i < 4; i = i + 1)
  452. {
  453. if(ISPRESSED(.m_keys[i]))
  454. {
  455. .m_dir = i;
  456. break;
  457. }
  458. }
  459. sleep(wait);
  460. }
  461. };
  462. return snake;
  463. };
  464. ///////////////////////////////////////////////////////////////////////////////////////////////////
  465. //
  466. // Game
  467. //
  468. g_game = table(
  469. // goodies
  470. m_gems = table(),
  471. m_levels = table(
  472. // level is speed, lower spawn time, upper spawn time, next level score
  473. table(1.0f, 10, 20, 500),
  474. table(2.0f, 8, 18, 1500),
  475. table(2.5f, 5, 16, 2700),
  476. table(3.0f, 4, 13, 3800),
  477. table(3.5f, 3, 10, 5000)
  478. ),
  479. m_gemTypes = table(
  480. CreateGemType("\4", 22, 50, 40, 3, fade0), // standard 1
  481. CreateGemType("\5", 17, 100, 30, 4, fade0), // standard 2
  482. CreateGemType("\6", 15, 200, 20, 5, fade1), // standard 3
  483. CreateGemType("\3", 30, 20, 15, 0, fadeRed, function() { .m_credits = .m_credits + 1; }, true), // 1up
  484. CreateGemType("\15", 45, 0, 15, 50, fade1) // bomb
  485. ),
  486. // snakes
  487. m_snakes = table(
  488. Snake(0, "lefty", 0, 8, 23, "\1", "\176", 1, 12, 0, table('D', 'W', 'A', 'S')),
  489. Snake(1, "righty", 0, 50, 23, "\2", "\177", 78, 12, 2, table('L', 'I', 'J', 'K'))
  490. ),
  491. // game
  492. m_level = 0,
  493. m_over = false,
  494. m_totalScore = 0,
  495. // timing
  496. m_speedup = 2.0f, // overall speed multiplier
  497. m_gameSpeed = 1.0f, // current game speed
  498. m_keyUpdateRate = 30.0f, // times per second
  499. m_snakeMoveRate = 4.0f // times per second
  500. );
  501. ///////////////////////////////////////////////////////////////////////////////////////////////////
  502. //
  503. // Main
  504. //
  505. ScoreMonitor = function()
  506. {
  507. .m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
  508. for(;;)
  509. {
  510. // update scores and levels.
  511. .m_totalScore = 0;
  512. foreach(snake in .m_snakes)
  513. {
  514. .m_totalScore = .m_totalScore + snake.m_score;
  515. }
  516. // are they at the next level
  517. if(.m_totalScore >= .m_levels[.m_level][3])
  518. {
  519. // level up.... have we finished
  520. .m_level = .m_level + 1;
  521. if(.m_level >= tableCount(.m_levels))
  522. {
  523. // you win....
  524. XYTEXT(0,0,"YOUWIN");
  525. sleep(5);
  526. .gameover = true;
  527. }
  528. else
  529. {
  530. // next level
  531. .m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
  532. }
  533. }
  534. XYTEXT(8, 0, format("LEVEL %d, SCORE %d, NEXT LEVEL TARGET %d", .m_level, .m_totalScore, .m_levels[.m_level][3]));
  535. if(showMemoryUsage)
  536. {
  537. XYTEXT(8, 1, format("mem %d Desired H: %d S: %d F: %d I: %d W: %d",
  538. sysGetMemoryUsage(),
  539. sysGetDesiredMemoryUsageHard(),
  540. sysGetDesiredMemoryUsageSoft(),
  541. sysGetStatsGCNumFullCollects(),
  542. sysGetStatsGCNumIncCollects(),
  543. sysGetStatsGCNumWarnings() ));
  544. }
  545. sleep(0.1);
  546. }
  547. };
  548. ///////////////////////////////////////////////////////////////////////////////////////////////////
  549. //
  550. // Title Screen
  551. //
  552. // todo
  553. ///////////////////////////////////////////////////////////////////////////////////////////////////
  554. //
  555. // Main
  556. //
  557. DrawScreen();
  558. // start the snakes
  559. foreach(snake in g_game.m_snakes)
  560. {
  561. snake.kht = snake:thread(snake.KeyHandler, g_game);
  562. snake.ut = snake:thread(snake.Update, g_game);
  563. }
  564. // start the gems
  565. egt = thread(EmitGems, g_game);
  566. rgt = thread(ReclaimGems, g_game);
  567. dgt = thread(DrawGems, g_game);
  568. mmt = thread(MoveMovers, g_game);
  569. smt = g_game:thread(ScoreMonitor);
  570. for(;;)
  571. {
  572. // test for escape key
  573. if(g_game.gameover or ISPRESSED(27))
  574. {
  575. // kill threads
  576. threadKill(egt);
  577. threadKill(rgt);
  578. threadKill(dgt);
  579. threadKill(smt);
  580. threadKill(mmt);
  581. foreach(snake in g_game.m_snakes)
  582. {
  583. threadKill(snake.kht);
  584. threadKill(snake.ut);
  585. }
  586. exit();
  587. }
  588. yield();
  589. }