| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- /*
- SNAKE
- by matty riek.
- */
- global showMemoryUsage = false;
- CURSOR(0,0);
- global g_sx = 80;
- global g_sy = 24;
- global g_border =
- "\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"
- "\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"
- "\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205\205";
- global LocFromXY = function(a_x, a_y) { return a_y * g_sx + a_x; };
- global XFromLoc = function(a_loc) { return a_loc % g_sx; };
- global YFromLoc = function(a_loc) { return a_loc / g_sx; };
- global background = CA.B_BLUE;
- global fade0 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_GREEN | CA.F_INTENSITY | CA.F_INTENSITY | background,
- CA.F_BLUE | background,
- CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_BLUE | CA.F_INTENSITY | background,
- CA.F_BLUE | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_BLUE|CA.F_GREEN | background,
- CA.F_BLUE | background,
- CA.F_BLUE | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background );
- global fade1 = table( CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_BLUE|CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background,
- CA.F_RED | CA.F_INTENSITY | background,
- CA.F_RED | background,
- CA.F_RED | CA.F_INTENSITY | background,
- CA.F_RED|CA.F_GREEN | CA.F_INTENSITY | background );
- global fadeRed = table( CA.F_RED|CA.F_INTENSITY | background , CA.F_RED | background);
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // CreateGemType
- //
- global CreateGemType = function(a_char, a_time, a_score, a_chance, a_length, a_cycle, a_pickup, a_mover)
- {
- 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);
- gem.Pickup = function()
- {
- };
- if(a_pickup)
- {
- gem.Pickup = a_pickup;
- }
- gem.i = 0;
- gem.Draw = function(x, y)
- {
- .i = .i + 1;
- if(.i >= tableCount(.m_cycle))
- {
- .i = 0;
- }
- CATTRIB(.m_cycle[.i]);
- XYTEXT(x, y, .m_char);
- CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
- };
- return gem;
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // DrawScreen
- //
- global DrawScreen = function()
- {
- //
- CATTRIB(background | CA.F_GREEN | CA.F_INTENSITY);
- // clear screen
- CLS();
- // draw border
- XYTEXT(1, 0, g_border);
- XYTEXT(1, g_sy - 1, g_border);
- ey = g_sy - 1;
- for(i = 1; i < ey; i = i + 1)
- {
- XYTEXT(0, i, "\186");
- XYTEXT(g_sx - 1, i, "\186");
- }
- XYTEXT(0, 0, "\201");
- XYTEXT(g_sx - 1, 0, "\187");
- XYTEXT(0, ey, "\200");
- XYTEXT(g_sx - 1, ey, "\188");
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // EmitGems
- //
- EmitGems = function(a_game)
- {
- // add a gem
- for(;;)
- {
- // choose a random sleep time
- sleep( randfloat(
- a_game.m_levels[a_game.m_level][1] / a_game.m_gameSpeed,
- a_game.m_levels[a_game.m_level][2] / a_game.m_gameSpeed)
- );
- // pick a random location
- x = randint(1, g_sx - 2);
- y = randint(1, g_sy - 2);
- loc = LocFromXY(x, y);
- // test if the location is on a existing gem or snake
- if(a_game.m_gems[loc])
- {
- continue;
- }
- onsnake = false;
- foreach(snake in a_game.m_snakes)
- {
- if(snake.IsAt(loc))
- {
- onsnake = true;
- break;
- }
- }
- if(onsnake) { continue; }
- // choose a gem
- found = false;
- while(!found)
- {
- foreach(gem in a_game.m_gemTypes)
- {
- if(randint(0, 100) < gem.m_chance)
- {
- found = true;
- break;
- }
- }
- }
- // add the gem
- gemInstance = table(m_gem = gem, m_expire = sysTime() + (gem.m_time / a_game.m_gameSpeed));
- a_game.m_gems[loc] = gemInstance;
- if(gem.m_mover)
- {
- gemInstance.m_dirX = randint(-1, 2);
- gemInstance.m_dirY = randint(-1, 2);
- }
- // draw the gem
- gem.Draw(x, y);
- }
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // ReclaimGems
- //
- ReclaimGems = function(a_game)
- {
- // add a gem
- for(;;)
- {
- time = sysTime();
- foreach(key and gem in a_game.m_gems)
- {
- if(time > gem.m_expire)
- {
- // remove from screen
- XYTEXT(XFromLoc(key), YFromLoc(key), " ");
- a_game.m_gems[key] = null;
- b = true;
- break;
- }
- }
- yield();
- }
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // DrawGems
- //
- DrawGems = function(a_game)
- {
- for(;;)
- {
- foreach(key and gem in a_game.m_gems)
- {
- x = XFromLoc(key);
- y = YFromLoc(key);
- gem.m_gem.Draw(x, y);
- }
- sleep(1 / 30.);
- }
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Move mover gems
- //
- MoveMovers = function(a_game)
- {
- for(;;)
- {
- movers = table();
- foreach(key and gem in a_game.m_gems)
- {
- if(gem.m_gem.m_mover)
- {
- x = XFromLoc(key);
- y = YFromLoc(key);
-
- XYTEXT(x, y, " ");
- x = x + gem.m_dirX;
- y = y + gem.m_dirY;
- if(x <= 1 or x >= g_sx - 2) { gem.m_dirX = -gem.m_dirX; }
- if(y <= 1 or y >= g_sy - 2) { gem.m_dirY = -gem.m_dirY; }
- gem.m_newLoc = LocFromXY(x, y);
- movers[key] = gem;
- }
- }
-
- foreach(key and gem in movers)
- {
- a_game.m_gems[key] = null;
- a_game.m_gems[gem.m_newLoc] = gem;
- }
- sleep(1 / 4.);
- }
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Snake
- //
- Snake = function(a_id, a_name, a_score, a_scoreX, a_scoreY, a_headChar, a_tailChar, a_startX, a_startY, a_startDir, a_keys)
- {
- snake = table(
- m_id = a_id,
- m_grow = 2,
- m_credits = 3,
- m_startX = a_startX,
- m_startY = a_startY,
- m_startDir = a_startDir,
- m_name = a_name,
- m_score = a_score,
- m_scoreX = a_scoreX,
- m_scoreY = a_scoreY,
- m_body = array(50),
- m_length = 0,
- m_dir = a_startDir, // 0 right, 1 up, 2 left, 3 down
- m_head = table(
- m_headChar = a_headChar,
- m_tailChar = a_tailChar,
- m_x = a_startX,
- m_y = a_startY
- ),
- m_keys = a_keys
- );
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Die
- //
- snake.Die = function() // return true if snake has no credits
- {
- // flash a death thinggy
- i = 10;
- while(i)
- {
- if(i & 1) {
- XYTEXT(.m_scoreX, .m_scoreY, format(" *** FATALITY *** ", .m_name));
- }
- else {
- XYTEXT(.m_scoreX, .m_scoreY, format(" ", .m_name));
- }
- sleep(0.2f);
- i = i - 1;
- }
- .UpdateScore();
- // clear the snake
- for(i = 0; i < .m_length; i = i + 1)
- {
- loc = .m_body[i];
- x = XFromLoc(loc);
- y = YFromLoc(loc);
- XYTEXT(x, y, " ");
- }
- XYTEXT(.m_head.m_x, .m_head.m_y, " ");
- // update the credits
- .m_credits = .m_credits - 1;
- if(.m_credits)
- {
- .m_head.m_x = .m_startX;
- .m_head.m_y = .m_startY;
- .m_dir = .m_startDir;
- .m_grow = 2;
- .m_length = 0;
- return false;
- }
- return true;
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // UpdateScore
- //
- snake.UpdateScore = function()
- {
- XYTEXT(.m_scoreX, .m_scoreY, format(" %d UP %s SCORE %d ", .m_credits, .m_name, .m_score));
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Draw
- //
- snake.Draw = function(a_oldX, a_oldY)
- {
- // clear the tail.
- if(.m_length > 0 and .grow == null)
- {
- loc = .m_body[.m_length - 1];
- y = YFromLoc(loc);
- x = XFromLoc(loc);
- XYTEXT(x, y, " ");
- }
- // grow the snake
- if(.grow)
- {
- .m_length = .m_length + 1;
- if(.m_length >= .m_body.Size())
- {
- .m_body.Resize(.m_body.Size() * 2);
- }
- .grow = false;
- }
- // move the body along.
- for(i = .m_length - 1; i > 0; i = i - 1)
- {
- .m_body[i] = .m_body[i - 1];
- }
- // put the new tail piece in
- if(.m_length)
- {
- .m_body[0] = LocFromXY(a_oldX, a_oldY);
- XYTEXT(a_oldX, a_oldY, .m_head.m_tailChar);
- }
- else
- {
- XYTEXT(a_oldX, a_oldY, " ");
- }
- // draw the new head
- XYTEXT(.m_head.m_x, .m_head.m_y, .m_head.m_headChar);
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // IsAt
- //
- snake.IsAt = function(a_loc)
- {
- for(i = 0; i < .m_length; i = i + 1)
- {
- if(a_loc == .m_body[i])
- {
- return true;
- }
- }
- if(a_loc == LocFromXY(.m_head.m_x, .m_head.m_y))
- {
- return true;
- }
- return false;
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Update
- //
- snake.Update = function(a_game)
- {
- member UpdateScore;
- UpdateScore();
- for(;;)
- {
- wait = 1.0f / (a_game.m_snakeMoveRate * a_game.m_gameSpeed);
- sleep(wait);
- x = .m_head.m_x;
- y = .m_head.m_y;
- if(.m_dir == 0) { x = x + 1; }
- else if(.m_dir == 1) { y = y - 1; }
- else if(.m_dir == 2) { x = x - 1; }
- else if(.m_dir == 3) { y = y + 1; }
- // did we collect a gem?
- loc = LocFromXY(x, y);
- gem = a_game.m_gems[loc];
- if(gem)
- {
- // update score
- .m_score = .m_score + gem.m_gem.m_score;
- .m_grow = gem.m_gem.m_length;
- pickup = gem.m_gem.Pickup;
- this:pickup();
- UpdateScore();
- // remove the gem
- a_game.m_gems[loc] = null;
- }
- // did we run into a wall??
- dead = false;
- if(x <= 0 or x >= g_sx - 1 or y <= 0 or y >= g_sy - 1)
- {
- dead = true;
- }
-
- // did we run into ourselves?
- if(!dead and .IsAt(loc))
- {
- dead = true;
- }
- // update position
- oldX = .m_head.m_x;
- oldY = .m_head.m_y;
- .m_head.m_x = x;
- .m_head.m_y = y;
- // did we run into another snake
- if(!dead)
- {
- foreach(snake in a_game.m_snakes)
- {
- if(snake != this and snake.IsAt(loc))
- {
- dead = true;
- break;
- }
- }
- }
- // did we die?
- if(dead)
- {
- .m_head.m_x = oldX;
- .m_head.m_y = oldY;
- die = .Die();
- .UpdateScore();
- if(die)
- {
- a_game.m_snakes[.m_id] = null;
- threadKill(.kht);
- exit();
- }
- }
- .Draw(oldX, oldY);
-
- if(.m_grow)
- {
- .grow = true;
- .m_grow = .m_grow - 1;
- }
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // KeyHandler
- //
- snake.KeyHandler = function(a_game)
- {
- wait = 1.0f / a_game.m_keyUpdateRate;
- for(;;)
- {
- for(i = 0; i < 4; i = i + 1)
- {
- if(ISPRESSED(.m_keys[i]))
- {
- .m_dir = i;
- break;
- }
- }
- sleep(wait);
- }
- };
- return snake;
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Game
- //
- g_game = table(
- // goodies
- m_gems = table(),
-
- m_levels = table(
-
- // level is speed, lower spawn time, upper spawn time, next level score
- table(1.0f, 10, 20, 500),
- table(2.0f, 8, 18, 1500),
- table(2.5f, 5, 16, 2700),
- table(3.0f, 4, 13, 3800),
- table(3.5f, 3, 10, 5000)
- ),
- m_gemTypes = table(
- CreateGemType("\4", 22, 50, 40, 3, fade0), // standard 1
- CreateGemType("\5", 17, 100, 30, 4, fade0), // standard 2
- CreateGemType("\6", 15, 200, 20, 5, fade1), // standard 3
- CreateGemType("\3", 30, 20, 15, 0, fadeRed, function() { .m_credits = .m_credits + 1; }, true), // 1up
- CreateGemType("\15", 45, 0, 15, 50, fade1) // bomb
- ),
- // snakes
- m_snakes = table(
- Snake(0, "lefty", 0, 8, 23, "\1", "\176", 1, 12, 0, table('D', 'W', 'A', 'S')),
- Snake(1, "righty", 0, 50, 23, "\2", "\177", 78, 12, 2, table('L', 'I', 'J', 'K'))
- ),
- // game
- m_level = 0,
- m_over = false,
- m_totalScore = 0,
- // timing
- m_speedup = 2.0f, // overall speed multiplier
- m_gameSpeed = 1.0f, // current game speed
- m_keyUpdateRate = 30.0f, // times per second
- m_snakeMoveRate = 4.0f // times per second
- );
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Main
- //
- ScoreMonitor = function()
- {
- .m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
- for(;;)
- {
- // update scores and levels.
- .m_totalScore = 0;
- foreach(snake in .m_snakes)
- {
- .m_totalScore = .m_totalScore + snake.m_score;
- }
- // are they at the next level
- if(.m_totalScore >= .m_levels[.m_level][3])
- {
- // level up.... have we finished
- .m_level = .m_level + 1;
- if(.m_level >= tableCount(.m_levels))
- {
- // you win....
- XYTEXT(0,0,"YOUWIN");
- sleep(5);
- .gameover = true;
- }
- else
- {
- // next level
- .m_gameSpeed = .m_speedup * .m_levels[.m_level][0];
- }
- }
- XYTEXT(8, 0, format("LEVEL %d, SCORE %d, NEXT LEVEL TARGET %d", .m_level, .m_totalScore, .m_levels[.m_level][3]));
- if(showMemoryUsage)
- {
- XYTEXT(8, 1, format("mem %d Desired H: %d S: %d F: %d I: %d W: %d",
- sysGetMemoryUsage(),
- sysGetDesiredMemoryUsageHard(),
- sysGetDesiredMemoryUsageSoft(),
- sysGetStatsGCNumFullCollects(),
- sysGetStatsGCNumIncCollects(),
- sysGetStatsGCNumWarnings() ));
- }
- sleep(0.1);
- }
- };
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Title Screen
- //
- // todo
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //
- // Main
- //
- DrawScreen();
- // start the snakes
- foreach(snake in g_game.m_snakes)
- {
- snake.kht = snake:thread(snake.KeyHandler, g_game);
- snake.ut = snake:thread(snake.Update, g_game);
- }
- // start the gems
- egt = thread(EmitGems, g_game);
- rgt = thread(ReclaimGems, g_game);
- dgt = thread(DrawGems, g_game);
- mmt = thread(MoveMovers, g_game);
- smt = g_game:thread(ScoreMonitor);
- for(;;)
- {
- // test for escape key
- if(g_game.gameover or ISPRESSED(27))
- {
- // kill threads
- threadKill(egt);
- threadKill(rgt);
- threadKill(dgt);
- threadKill(smt);
- threadKill(mmt);
- foreach(snake in g_game.m_snakes)
- {
- threadKill(snake.kht);
- threadKill(snake.ut);
- }
- exit();
- }
- yield();
- }
|