Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1571 lines (1299 sloc) 35.7 KB
#~
# compiler: obc -src 2d_game_13.obs -lib collect.obl,sdl_game.obl,sdl2.obl -dest 2d_game_13.obe
# run: obr 2d_game_13.obe
~#
use System.Concurrency;
use Collection;
use Game.SDL2;
use Game.Framework;
class Game {
# game framework
@framework : GameFramework;
@initialized : Bool;
@base_path : String;
# player
@player : Player;
# music and sound bites
@music : MixMusic;
@gem_chunk : MixChunk;
@start_chunk : MixChunk;
@won_chunk : MixChunk;
@won_channel : Int;
@die_chunk : MixChunk;
@die_channel : Int;
# background image
@background : ImageSprite;
@lava : ImageSprite;
@lava_clip1 : Rect;
@lava_clip2 : Rect;
# game level
@level : Level;
@timer : Int;
@timer_text : TextSprite;
@current_level : Int;
# scores
@score : Int;
@score_text : TextSprite;
@high_score : Int;
@high_score_text : TextSprite;
# status flags
@game_over : Bool;
@won_game : Bool;
@quit : Bool;
@hard_quit : Bool;
@paused : Bool;
@level_mutex : static : ThreadMutex;
function : Main(args : String[]) ~ Nil {
game := Game->New();
@level_mutex := ThreadMutex->New("level_lock");
while(game->TileScreen()) {
game->Play();
};
game->Quit();
}
New() {
@initialized := true;
@framework := GameFramework->New(GameConsts->SCREEN_WIDTH, GameConsts->SCREEN_HEIGHT, "Secret Platform");
@framework->SetClearColor(Color->New(255, 255, 255));
@base_path := Runtime->GetProperty("install_dir");
@base_path += "/examples";
music_path := String->New(@base_path);
music_path += "/media/music.wav";
@music := MixMusic->New(music_path);
if(@music->IsNull()) {
"--- Cannot load music: '{$music_path}'! ---"->ErrorLine();
@initialized := false;
};
gem_path := String->New(@base_path);
gem_path += "/media/gem.wav";
@gem_chunk := MixChunk->New(gem_path);
if(@gem_chunk->IsNull()) {
"--- Cannot load music: '{$gem_path}'! ---"->ErrorLine();
@initialized := false;
};
start_path := String->New(@base_path);
start_path += "/media/start.wav";
@start_chunk := MixChunk->New(start_path);
if(@start_chunk->IsNull()) {
"--- Cannot load music: '{$start_path}'! ---"->ErrorLine();
@initialized := false;
};
won_path := String->New(@base_path);
won_path += "/media/luck.wav";
@won_chunk := MixChunk->New(won_path);
if(@won_chunk->IsNull()) {
"--- Cannot load music: '{$won_path}'! ---"->ErrorLine();
@initialized := false;
};
die_path := String->New(@base_path);
die_path += "/media/die.wav";
@die_chunk := MixChunk->New(die_path);
if(@die_chunk->IsNull()) {
"--- Cannot load music: '{$die_path}'! ---"->ErrorLine();
@initialized := false;
};
@won_channel := -1;
@die_channel := -1;
@current_level := 1;
}
method : TileScreen() ~ Bool {
e := @framework->GetEvent();
@high_score_text := @framework->AddTextSprite();
buffer := "High Score: ";
buffer += @high_score->ToString();
@high_score_text->RenderedText(buffer);
font_path := String->New(@base_path);
if(font_path = Nil) {
"Failed to load font!"->ErrorLine();
return false;
};
font_path += "/../lib/sdl/fonts/lazy.ttf";
font := Font->New(font_path, 36);
if(font->IsNull()) {
"Failed to load font '{$font_path}'!"->ErrorLine();
return false;
};
red_color := Color->New(255, 0, 0);
black_color := Color->New(255, 255, 255);
play_text := @framework->AddTextSprite();
play_text->SetFont(font);
quit_text := @framework->AddTextSprite();
quit_text->SetFont(font);
index := 0;
quit := false;
while(<>quit & <>@hard_quit) {
@framework->FrameStart();
# process input
while(e->Poll() <> 0) {
# joystick
if(e->GetType() = EventType->SDL_QUIT) {
@hard_quit := true;
}
else if(e->GetType() = EventType->SDL_JOYHATMOTION) {
if(e->GetJHat()->GetWhich() = 0 ) {
select(e->GetJHat()->GetValue()) {
label 1:
label 4: {
if(index = 0) {
index := 1;
}
else {
index := 0;
};
}
};
};
}
# keyboard
else if(e->GetType() = EventType->SDL_KEYDOWN & e->GetKey()->GetRepeat() = 0) {
select(e->GetKey()->GetKeysym()->GetScancode()) {
label Scancode->SDL_SCANCODE_UP:
label Scancode->SDL_SCANCODE_DOWN: {
if(index = 0) {
index := 1;
}
else {
index := 0;
};
}
label Scancode->SDL_SCANCODE_RETURN: {
@won_game := false;
return index = 0 ? true : false;
}
label Scancode->SDL_SCANCODE_P: {
@won_game := false;
return true;
}
label Scancode->SDL_SCANCODE_Q:
label Scancode->SDL_SCANCODE_ESCAPE: {
@won_game := false;
return false;
}
};
}
else if(e->GetType() = EventType->SDL_JOYBUTTONDOWN) {
# 'a' button
if(e->GetJButton()->GetButton() = 0) {
@won_game := false;
return index = 0 ? true : false;
};
};
};
if(index = 0) {
play_text->RenderedText("(P)lay!", red_color);
quit_text->RenderedText("(Q)uit");
}
else {
play_text->RenderedText("(P)lay!");
quit_text->RenderedText("(Q)uit", red_color);
};
@framework->Clear();
@high_score_text->Render(10, 10);
if((@won_channel < 0 & @die_channel < 0) | (<>Mixer->Playing(@won_channel) & <>Mixer->Playing(@die_channel))) {
play_text->Render(GameConsts->SCREEN_WIDTH / 2 - play_text->GetWidth() /2, GameConsts->SCREEN_HEIGHT / 2 - 32);
quit_text->Render(GameConsts->SCREEN_WIDTH / 2 - quit_text->GetWidth() /2, GameConsts->SCREEN_HEIGHT / 2 + 32);
}
else if(@won_game) {
play_text->RenderedText("Winner!", red_color);
play_text->Render(GameConsts->SCREEN_WIDTH / 2 - play_text->GetWidth() /2, GameConsts->SCREEN_HEIGHT / 2 - 32);
}
else {
play_text->RenderedText("Game Over!", red_color);
play_text->Render(GameConsts->SCREEN_WIDTH / 2 - play_text->GetWidth() /2, GameConsts->SCREEN_HEIGHT / 2 - 32);
};
@framework->FrameEnd();
@framework->Show();
};
leaving {
@framework->FreeShapes();
};
@won_game := false;
return false;
}
method : Play() ~ Nil {
if(@initialized) {
@quit := false;
@score := 0;
@player := Player->New(@self);
@level := Level->New(@self, @current_level);
e := @framework->GetEvent();
frame_count := 0;
@start_chunk->PlayChannel(-1, 0);
if(MixMusic->Playing()) {
MixMusic->Resume();
}
else {
@music->FadeInMusic(-1, 2000);
};
background_path := String->New(@base_path);
background_path += "/media/background.png";
@background := @framework->AddImageSprite(background_path);
fire_path := String->New(@base_path);
fire_path += "/media/fire.png";
@lava := @framework->AddImageSprite(fire_path);
@lava_clip1 := Rect->New(0, 0, 800, 64);
@lava_clip2 := Rect->New(10, 0, 800, 64);
@timer := GameConsts->LEVEL_TIME;
buffer := "Time: ";
buffer += @timer->ToString();
@timer_text := @framework->AddTextSprite();
@timer_text->RenderedText(buffer);
@score_text := @framework->AddTextSprite();
buffer := "Level: ";
buffer += @current_level;
buffer += ", Score: ";
buffer += @score->ToString();
@score_text->RenderedText(buffer);
# start game loop
while(<>@quit & <>@hard_quit) {
@framework->FrameStart();
@framework->Clear();
# process user input
while(e->Poll() <> 0) {
if(e->GetType() = EventType->SDL_QUIT) {
@hard_quit := true;
};
ProcessInput(e);
};
if(<>@paused) {
# update model
@player->Update();
@level->Update();
UpdateView();
# draw
DrawBackground();
@level->Draw(frame_count);
@player->Draw();
DrawLava(frame_count);
@timer_text->Render(685, 10);
@score_text->Render(10, 10);
# show
@framework->Show();
@framework->FrameEnd();
frame_count += 1;
if(frame_count >= @framework->GetFps()) {
if(@game_over) {
@timer_text->RenderedText("Game Over!");
}
else {
@timer -= 1;
if(@timer < 0) {
GameOver();
}
else {
buffer := "Time: ";
buffer += @timer->ToString();
@timer_text->RenderedText(buffer);
};
};
frame_count := 0;
};
};
};
}
else {
"--- Error Initializing Environment ---"->ErrorLine();
return;
};
leaving {
@framework->FreeShapes();
};
}
method : Quit() ~ Nil {
@music->Free();
@gem_chunk->Free();
@start_chunk->Free();
@won_chunk->Free();
@die_chunk->Free();
@framework->Quit();
}
method : public : UpdateView() ~ Nil {
shift := @level->GetShift();
if(@player->GetRight() >= GameConsts->PLAYER_BOX_MAX) {
diff := @player->GetRight() - GameConsts->PLAYER_BOX_MAX;
@player->SetRight(GameConsts->PLAYER_BOX_MAX);
if(shift > GameConsts->LEVEL_WIDTH_MAX) {
@level->ShiftView(-1 * diff);
};
};
if(@player->GetLeft() <= GameConsts->PLAYER_BOX_MIN) {
diff := GameConsts->PLAYER_BOX_MIN - @player->GetLeft();
@player->SetLeft(GameConsts->PLAYER_BOX_MIN);
if(shift < 0) {
@level->ShiftView(diff);
};
};
}
method : public : GetFramework() ~ GameFramework {
return @framework;
}
method : public : GetPlayer() ~ Player {
return @player;
}
method : public : GetCurrentLevel() ~ Int {
return @current_level;
}
method : public : GetBasePath() ~ String {
return @base_path;
}
method : public : AddScore(points : Int) ~ Nil {
@score += points;
buffer := "Level: ";
buffer += @current_level;
buffer += ", Score: ";
buffer += @score->ToString();
@score_text->RenderedText(buffer);
@gem_chunk->PlayChannel(-1, 0);
}
method : public : Won() ~ Nil {
@won_channel := @won_chunk->PlayChannel(-1, 0);
if(@score > @high_score) {
@high_score := @score;
};
if(@current_level >= GameConsts->MAX_LEVELS) {
MixMusic->Pause();
@won_game := true;
@quit := true;
};
critical(@level_mutex) {
@current_level += 1;
@level := Level->New(@self, @current_level);
@score += @timer * GameConsts->TIME_POINTS;
buffer := "Level: ";
buffer += @current_level;
buffer += ", Score: ";
buffer += @score->ToString();
@score_text->RenderedText(buffer);
@player->Won();
@timer := GameConsts->LEVEL_TIME - @current_level * GameConsts->LEVEL_DEC_TIME;
};
}
method : public : GameOver() ~ Nil {
@die_channel := @die_chunk->PlayChannel(-1, 0);
@player->GameOver();
@level->GameOver();
MixMusic->Pause();
if(@score > @high_score) {
@high_score := @score;
};
@current_level := 1;
@quit := true;
}
method : DrawBackground() ~ Nil {
shift := @level->GetShift() / 2;
@background->Render(shift, 0);
@background->Render(shift + @background->GetWidth(), 0);
}
method : DrawLava(frame_count : Int) ~ Nil {
shift := @level->GetShift() / 3;
y_pos := GameConsts->SCREEN_HEIGHT - 64;
if(frame_count % 10 = 0) {
@lava->Render(shift, y_pos, @lava_clip1);
@lava->Render(shift + @lava->GetWidth(), y_pos, @lava_clip1);
}
else {
@lava->Render(shift, y_pos, @lava_clip2);
@lava->Render(shift + @lava->GetWidth(), y_pos, @lava_clip2);
};
}
method : ProcessInput(e : Event) ~ Nil {
# joystick
if(e->GetType() = EventType->SDL_JOYHATMOTION) {
if(e->GetJHat()->GetWhich() = 0 ) {
select(e->GetJHat()->GetValue()) {
# release pad
label 0: {
@player->Stop();
}
# right pad
label 2: {
@player->GoRight();
}
# left pad
label 8: {
@player->GoLeft();
}
};
};
}
else if(e->GetType() = EventType->SDL_JOYBUTTONDOWN) {
if(e->GetJButton()->GetButton() = 0) {
@player->Jump(true);
}
else if(e->GetJButton()->GetButton() = 7) {
if(@paused) {
@paused := false;
}
else {
@paused := true;
};
};
# else if(e->GetJButton()->GetButton() = 1) { ... };
}
else if(e->GetType() = EventType->SDL_JOYBUTTONUP) {
if(e->GetJButton()->GetButton() = 0) {
@player->Jump(false);
};
# B button
# else if(e->GetJButton()->GetButton() = 1) { ... };
}
else if(e->GetType() = EventType->SDL_KEYDOWN) {
select(e->GetKey()->GetKeysym()->GetScancode()) {
label Scancode->SDL_SCANCODE_LEFT: {
@player->GoLeft();
}
label Scancode->SDL_SCANCODE_RIGHT: {
@player->GoRight();
}
label Scancode->SDL_SCANCODE_UP: {
@player->Jump(true);
}
label Scancode->SDL_SCANCODE_ESCAPE: {
GameOver();
}
label Scancode->SDL_SCANCODE_SPACE: {
if(@paused) {
@paused := false;
}
else {
@paused := true;
};
}
};
}
else if(e->GetType() = EventType->SDL_KEYUP) {
select(e->GetKey()->GetKeysym()->GetScancode()) {
label Scancode->SDL_SCANCODE_LEFT:
label Scancode->SDL_SCANCODE_RIGHT: {
@player->Stop();
}
label Scancode->SDL_SCANCODE_UP: {
@player->Jump(false);
}
};
};
}
}
class Player {
@rect : AnimatedImageSprite;
@game : Game;
@player_dir : Int;
@loc : Vector2;
@vel : Vector2;
@acc : Vector2;
@jumped : Bool;
@jump_max : Float;
@hit_top : Bool;
@level : Level;
@game_over : Bool;
@hanging_platform : HangingPlatform;
New(game : Game) {
@game := game;
framework := game->GetFramework();
base_path := game->GetBasePath();
clips := Vector->New();
clips->AddBack(Rect->New(0, 0, 32, 52));
clips->AddBack(Rect->New(32, 0, 32, 52));
clips->AddBack(Rect->New(64, 0, 32, 52));
clips->AddBack(Rect->New(96, 0, 32, 52));
clips->AddBack(Rect->New(128, 0, 32, 52));
clips->AddBack(Rect->New(160, 0, 32, 52));
clips->AddBack(Rect->New(192, 0, 32, 52));
clips->AddBack(Rect->New(224, 0, 32, 52));
player_path := String->New(base_path);
player_path += "/media/player.png";
@rect := framework->AddAnimatedImageSprite(player_path, clips);
@loc := Vector2->New(GameConsts->PLAYER_BOX_MIN->As(Float), GameConsts->SCREEN_HEIGHT / 2.0);
@vel := Vector2->New();
@acc := Vector2->New();
}
method : public : GameOver() ~ Nil {
@game_over := true;
}
method : public : Won() ~ Nil {
@loc := Vector2->New(GameConsts->PLAYER_BOX_MIN->As(Float) + 75, GameConsts->SCREEN_HEIGHT / 2);
}
method : public : SetLevel(level : Level) ~ Nil {
@level := level;
}
method : public : GetRect() ~ AnimatedImageSprite {
return @rect;
}
method : public : GetLeft() ~ Int {
return @loc->GetX() - @rect->GetWidth() / 2;
}
method : public : GetRight() ~ Int {
return @loc->GetX() + @rect->GetWidth() / 2;
}
method : public : SetLeft(x : Int) ~ Nil {
@loc->SetX(x + @rect->GetWidth() / 2);
}
method : public : SetRight(x : Int) ~ Nil {
@loc->SetX(x - @rect->GetWidth() / 2);
}
method : public : GoLeft() ~ Nil {
if(@hanging_platform = Nil) {
@player_dir := -1;
if(<>@game_over & @rect->GetFlip() = RendererFlip->SDL_FLIP_NONE) {
@rect->SetFlip(RendererFlip->SDL_FLIP_HORIZONTAL);
};
};
}
method : public : GoRight() ~ Nil {
if(@hanging_platform = Nil) {
@player_dir := 1;
if(<>@game_over & @rect->GetFlip() = RendererFlip->SDL_FLIP_HORIZONTAL) {
@rect->SetFlip(RendererFlip->SDL_FLIP_NONE);
};
};
}
method : public : Stop() ~ Nil {
@player_dir := 0;
}
method : public : Jump(jumped : Bool) ~ Nil {
@jumped := jumped;
}
method : public : Update() ~ Nil {
if(@game_over) {
if(@rect->GetTop() < GameConsts->SCREEN_HEIGHT) {
@rect->AddY(1);
};
}
else {
Movement();
CheckPlatforms();
CheckGems();
CheckSpiders();
CheckHangingPlatforms();
CheckSign();
};
CheckFloorCeiling();
}
method : Movement() ~ Nil {
if(@hanging_platform <> Nil) {
base_bottom := @hanging_platform->GetBottom() + @hanging_platform->GetHeight();
if(@jumped) {
@jumped := false;
@loc->Set(@hanging_platform->GetLeft(), base_bottom + @rect->GetHeight());
@hanging_platform := Nil;
}
else {
@loc->Set(@hanging_platform->GetLeft(), base_bottom + @rect->GetHeight() / 2);
};
}
else {
@acc->SetX(@player_dir * 0.5);
# movement logic
if(@hit_top) {
@acc->SetY(80);
@hit_top := false;
}
else {
if(@jumped & @jump_max < 2.75) {
@acc->SetY(-5.3);
@jump_max += 0.83;
}
else {
@acc->SetY(0.65);
};
};
@acc->Add(Vector2->Mul(@vel, -0.0925));
@vel->Add(Vector2->Mul(@acc, 0.75));
@loc->Add(@vel);
@acc->Zero();
};
}
method : public : CheckPlatforms() ~ Nil {
# reposition x-axis
@rect->SetCenterX(@loc->GetX());
collides := @level->PlatformCollisions(@self);
if(collides->Size() > 0 #~ & @hanging_platform <> Nil ~#) {
foo := @hanging_platform <> Nil;
"--- Hit Platform while Hanging hanging hanging={$foo}, Should Fall! ---"->PrintLine();
};
each(i : collides) {
platform := collides->Get(i)->As(StaticPlatform);
if(@player_dir = 1) {
@rect->SetRight(platform->GetLeft());
@loc->SetX(platform->GetLeft() - @rect->GetWidth() / 2);
@vel->MulX(0);
}
else if(@player_dir = -1) {
@rect->SetLeft(platform->GetRight());
@loc->SetX(platform->GetRight() + @rect->GetWidth() / 2);
@vel->MulX(0);
};
};
# reposition y-axis
@rect->SetBottom(@loc->GetY() - @rect->GetHeight() / 2);
collides := @level->PlatformCollisions(@self);
each(i : collides) {
platform := collides->Get(i)->As(StaticPlatform);
if(@vel->GetY() < 0.01) {
@rect->SetTop(platform->GetBottom());
@loc->SetY(platform->GetBottom() + @rect->GetHeight() / 2);
@jump_max := 3.0;
@hit_top := true;
}
else if(@vel->GetY() > 0.01) {
@rect->SetBottom(platform->GetTop());
@loc->SetY(platform->GetTop() + @rect->GetHeight() / 2);
@vel->SetY(0);
@jump_max := 0.0;
};
};
}
method : public : CheckGems() ~ Nil {
collides := @level->GemsCollisions(@self);
each(i : collides) {
gem := collides->Get(i)->As(Gem);
gem->Hide();
@game->AddScore(GameConsts->GEM_POINTS + @game->GetCurrentLevel() * 5);
};
}
method : public : CheckSign() ~ Nil {
if(@level->SignCollision(@self)) {
@game->Won();
};
}
method : public : CheckSpiders() ~ Nil {
collides := @level->SpiderCollisions(@self);
each(i : collides) {
spider := collides->Get(i)->As(Spider);
spider->Hide();
GameOver();
};
}
method : public : CheckHangingPlatforms() ~ Nil {
if(@hanging_platform = Nil) {
collides := @level->HangingPlatformCollisions(@self);
each(i : collides) {
@hanging_platform := collides->Get(i)->As(HangingPlatform);
return;
};
@hanging_platform := Nil;
};
}
method : public : CheckFloorCeiling() ~ Nil {
if(@rect->GetTop() >= GameConsts->SCREEN_HEIGHT) {
@rect->SetTop(GameConsts->SCREEN_HEIGHT);
@vel->SetY(0);
@game->GameOver();
}
else if(@loc->GetY() < 0) {
@loc->SetY(0);
@vel->SetY(0);
};
}
method : public : Draw() ~ Nil {
if(@game_over) {
@rect->Render(6);
}
else if(@hanging_platform <> Nil) {
@rect->Render(5);
}
else if(@jump_max > 0.0) {
@rect->Render(5);
}
else if(@player_dir = 0) {
@rect->Render(4);
}
else {
offset := @level->GetShift() + @loc->GetX();
index := Int->Abs((offset / 15)->As(Int) % 4);
@rect->Render(index);
};
}
}
class StaticPlatform {
@rect : Rectangle;
New(x : Int, y : Int, rect : Rectangle) {
@rect := rect;
@rect->SetFill(true);
@rect->SetColor(Color->New(50, 51, 52));
@rect->Set(x, y);
}
method : public : AddX(x : Int) ~ Nil {
@rect->AddX(x);
}
method : public : GetLeft() ~ Int {
return @rect->GetLeft();
}
method : public : GetRight() ~ Int {
return @rect->GetRight();
}
method : public : GetTop() ~ Int {
return @rect->GetTop();
}
method : public : GetBottom() ~ Int {
return @rect->GetBottom();
}
method : public : GetWidth() ~ Int {
return @rect->GetWidth();
}
method : public : Draw() ~ Nil {
@rect->Render();
}
method : public : Collides(rect : AnimatedImageSprite) ~ Bool {
return @rect->GetPosition()->Overlaps(rect->GetPosition());
}
}
class MovingPlatform from StaticPlatform {
@change_x : Int;
@change_y : Int;
@boundary_top : Int;
@boundary_bottom : Int;
@boundary_left : Int;
@boundary_right : Int;
@player : Player;
@level : Level;
New(x : Int, y : Int, rect : Rectangle, player : Player, level : Level) {
Parent(x, y, rect);
@rect->SetColor(Color->New(207, 181, 59));
@player := player;
@level := level;
@boundary_top := y - 200;
if(@boundary_top < 60) {
@boundary_top := 80;
};
@boundary_bottom := y + 200;
if(@boundary_bottom > 560) {
@boundary_bottom := 560;
};
@change_y := 1;
}
method : public : GetChangeLeft() ~ Int {
return @change_x;
}
method : public : SetLevel(level : Level) ~ Nil {
@level := level;
}
method : public : native : Update() ~ Nil {
@rect->AddX(@change_x);
if(Collides(@player->GetRect())) {
if(@change_x < 0) {
@player->GetRect()->SetRight(@rect->GetLeft());
}
else {
@player->GetRect()->SetLeft(@rect->GetRight());
};
};
@rect->AddY(@change_y);
if(Collides(@player->GetRect())) {
if(@change_y < 0) {
@player->GetRect()->SetBottom(@rect->GetTop());
}
else {
@player->GetRect()->SetTop(@rect->GetBottom());
};
};
if(@rect->GetBottom() > @boundary_bottom | @rect->GetTop() < @boundary_top) {
@change_y *= -1;
};
cur_pos := @rect->GetLeft() - @level->GetShift();
if(cur_pos < @boundary_left | cur_pos > @boundary_right) {
@change_x *= -1;
};
}
}
class Level {
@game : Game;
@player : Player;
@platforms : Vector;
@gems : Vector;
@spiders : Vector;
@hanging_platforms : Vector;
@shift_x : Int;
@game_over : Bool;
@end_post : SignPost;
@current_level : Int;
New(game : Game, hardness : Int) {
@game := game;
@current_level := hardness;
@player := game->GetPlayer();
@player->SetLevel(@self);
base_path := @game->GetBasePath();
framework := game->GetFramework();
@platforms := GeneratePlatforms(base_path, framework);
@gems := GenerateGems(base_path, framework);
@hanging_platforms := GenerateHangingPlatform(base_path, framework);
@spiders := GeneratSpiders(base_path, framework);
}
method : public : GameOver() ~ Nil {
@game_over := true;
}
method : GeneratePlatforms(base_path : String, framework : GameFramework) ~ Vector {
platforms := Vector->New();
width := 110;
height := 20;
x_base_pos := GameConsts->PLAYER_BOX_MIN + GameConsts->PLAYER_BOX_MIN / 2;
platforms->AddBack(StaticPlatform->New(x_base_pos, 525, framework->AddRectangle(width, height)));
x_base_pos += 110 + Int->Random(10, 20);
for(i := 0; i < 10; i += 1;) {
prev_plat := platforms->Get(i)->As(StaticPlatform);
height := Int->Random(10, 25);
x_offset := Int->Random(10, 35);
y_pos := NextYPos(prev_plat, 20, 40);
if(Int->Random(1) = 0) {
platforms->AddBack(StaticPlatform->New(x_base_pos + x_offset, y_pos, framework->AddRectangle(width, height)));
}
else {
platforms->AddBack(MovingPlatform->New(x_base_pos + x_offset, y_pos, framework->AddRectangle(width, height), @player, @self));
};
x_base_pos += 110 + Int->Random(45, 60);
};
last_plat := StaticPlatform->New(1950, 525, framework->AddRectangle(110, 20));
platforms->AddBack(last_plat);
sign_path := String->New(base_path);
sign_path += "/media/sign.png";
@end_post := SignPost->New(1990, 462, framework->AddAnimatedImageSprite(sign_path));
return platforms;
}
method : NextYPos(prev_plat : StaticPlatform, min : Int, max : Int) ~ Int {
y_pos := prev_plat->GetTop() + Int->Random(min, max);
if(y_pos < 60) {
y_pos += Int->Random(60, 80);
}
else if(y_pos > 540) {
y_pos -= Int->Random(60, 80);
};
return y_pos;
}
method : GenerateHangingPlatform(base_path : String, framework : GameFramework) ~ Vector {
logs := Vector->New();
log_path := String->New(base_path);
log_path += "/media/log.png";
ring_path := String->New(base_path);
ring_path += "/media/ring.png";
x_offset := 100;
y_offset := Int->Random(225, 275);
for(i := 0; i < 3; i += 1;) {
logs->AddBack(HangingPlatform->New(x_offset, y_offset,
framework->AddImageSprite(log_path),
framework->AddImageSprite(ring_path)));
x_offset += 600;
};
return logs;
}
method : GenerateGems(base_path : String, framework : GameFramework) ~ Vector {
gems := Vector->New();
gems_path := String->New(base_path);
gems_path += "/media/gems.png";
clips := Vector->New();
clips->AddBack(Rect->New(0, 0, 32, 32));
clips->AddBack(Rect->New(32, 0, 32, 32));
clips->AddBack(Rect->New(64, 0, 32, 32));
clips->AddBack(Rect->New(96, 0, 32, 32));
clips->AddBack(Rect->New(128, 0, 32, 32));
clips->AddBack(Rect->New(160, 0, 32, 32));
each(i : @platforms) {
flip := Int->Random(3);
if(flip % 3 = 0) {
platform := @platforms->Get(i)->As(StaticPlatform);
x_offset : Int;
value := Int->Random(10, 30);
if(i % 2 = 0) {
x_offset := platform->GetWidth() / 2 + value;
}
else {
x_offset := platform->GetWidth() / 2 - value;
};
x_offset += platform->GetLeft();
y_offset := platform->GetTop() - 32 - Int->Random(10, 100);
gems->AddBack(Gem->New(x_offset, y_offset, framework->AddAnimatedImageSprite(gems_path, clips)));
};
};
return gems;
}
method : GeneratSpiders(base_path : String, framework : GameFramework) ~ Vector {
spiders := Vector->New();
spider_path := String->New(base_path);
spider_path += "/media/spider.png";
total := 0;
for(i := 1; i < @platforms->Size() - 1; i+= 1;) {
value := GameConsts->MAX_LEVELS - @current_level;
flip := Int->Random(value);
if(flip % value = 0) {
total += 1;
platform := @platforms->Get(i)->As(StaticPlatform);
if(<>platform->TypeOf(MovingPlatform)) {
x_offset : Int;
value := Int->Random(10, 30);
if(i % 2 = 0) {
x_offset := platform->GetWidth() / 2 + value;
}
else {
x_offset := platform->GetWidth() / 2 - value;
};
y_offset := platform->GetTop() - 18;
spiders->AddBack(Spider->New(platform->GetLeft() + x_offset, y_offset, framework->AddAnimatedImageSprite(spider_path), @current_level));
};
};
};
return spiders;
}
method : public : GetShift() ~ Int {
return @shift_x;
}
method : public : ShiftView(shift_x : Int) ~ Nil {
@shift_x += shift_x;
each(i : @platforms) {
platform := @platforms->Get(i)->As(StaticPlatform);
platform->AddX(shift_x);
};
each(i : @gems) {
platform := @gems->Get(i)->As(Gem);
platform->AddX(shift_x);
};
each(i : @hanging_platforms) {
hanging_platform := @hanging_platforms->Get(i)->As(HangingPlatform);
hanging_platform->AddX(shift_x);
};
each(i : @spiders) {
platform := @spiders->Get(i)->As(Spider);
platform->AddX(shift_x);
};
@end_post->AddX(shift_x);
}
method : public : SignCollision(player : Player) ~ Bool {
return @end_post->Collides(player->GetRect());
}
method : public : PlatformCollisions(player : Player) ~ Vector {
collides := Vector->New();
each(i : @platforms) {
obstacle := @platforms->Get(i)->As(StaticPlatform);
if(obstacle->Collides(player->GetRect())) {
collides->AddBack(obstacle);
};
};
return collides;
}
method : public : GemsCollisions(player : Player) ~ Vector {
collides := Vector->New();
each(i : @gems) {
obstacle := @gems->Get(i)->As(Gem);
if(obstacle->Collides(player->GetRect())) {
collides->AddBack(obstacle);
};
};
return collides;
}
method : public : HangingPlatformCollisions(player : Player) ~ Vector {
collides := Vector->New();
each(i : @hanging_platforms) {
obstacle := @hanging_platforms->Get(i)->As(HangingPlatform);
if(obstacle->Collides(player->GetRect())) {
collides->AddBack(obstacle);
};
};
return collides;
}
method : public : SpiderCollisions(player : Player) ~ Vector {
collides := Vector->New();
each(i : @spiders) {
obstacle := @spiders->Get(i)->As(Spider);
if(obstacle->Collides(player->GetRect())) {
collides->AddBack(obstacle);
};
};
return collides;
}
method : public : Update() ~ Nil {
each(i : @platforms) {
platform := @platforms->Get(i);
if(platform->TypeOf(MovingPlatform)) {
platform->As(MovingPlatform)->Update();
};
};
each(i : @spiders) {
@spiders->Get(i)->As(Spider)->Update();
};
each(i : @hanging_platforms) {
@hanging_platforms->Get(i)->As(HangingPlatform)->Update();
};
}
method : public : Draw(frame_count : Int) ~ Nil {
if(<>@game_over) {
each(i : @gems) {
gem := @gems->Get(i)->As(Gem);
if(<>(gem->GetRight() < 0 | gem->GetLeft() > GameConsts->SCREEN_WIDTH)) {
gem->Draw(frame_count);
};
};
each(i : @platforms) {
platform := @platforms->Get(i)->As(StaticPlatform);
if(<>(platform->GetRight() < 0 | platform->GetLeft() > GameConsts->SCREEN_WIDTH)) {
platform->Draw();
};
};
each(i : @spiders) {
spider := @spiders->Get(i)->As(Spider);
if(<>(spider->GetRight() < 0 | spider->GetLeft() > GameConsts->SCREEN_WIDTH)) {
spider->Draw(frame_count);
};
};
each(i : @hanging_platforms) {
hanging_platform := @hanging_platforms->Get(i)->As(HangingPlatform);
if(<>(hanging_platform->GetRight() < -300 | hanging_platform->GetLeft() > GameConsts->SCREEN_WIDTH + 300)) {
hanging_platform->Draw(frame_count);
};
};
if(<>(@end_post->GetRight() < 0 | @end_post->GetLeft() > GameConsts->SCREEN_WIDTH)) {
@end_post->Draw(frame_count);
};
};
}
}
interface Actor {
method : virtual : public : Draw(frame_count : Int) ~ Nil;
method : virtual : public : Collides(rect : ImageSprite) ~ Bool;
}
class HangingPlatform implements Actor {
@beam : ImageSprite;
@beam_x_start : Int;
@beam_x_end : Int;
@grip : ImageSprite;
@grip_y : Float;
@grip_x : Float;
@grip_dir : Int;
@grip_x_diff : Float;
@grip_y_diff : Float;
New(x : Int, y : Int, beam : ImageSprite, handle_grip : ImageSprite) {
angle := Int->Random(0, 35);
@beam := beam;
@grip := handle_grip;
rad_angle := Float->ToRadians(angle);
sin_angle := Float->Sin(rad_angle);
cos_angle := Float->Cos(rad_angle);
center_x := x + @beam->GetWidth() / 2;
center_y := y + @beam->GetHeight() / 2;
tran_x := center_x + (x - center_x) * cos_angle - (y - center_y) * sin_angle;
tran_y := center_y + (x - center_x) * sin_angle + (y - center_y) * cos_angle;
# "old=[{$x},{$y}], new=[{$tran_x},{$tran_y}]"->PrintLine();
@beam->Set(x, y);
@beam->SetAngle(angle);
@grip_dir := 1;
@grip_y_diff := Float->Tan(rad_angle);
@grip_x := tran_x;
@grip_y := tran_y;
@grip->Set(@grip_x, @grip_y);
@beam_x_start := tran_x;
@beam_x_end := @beam_x_start + @beam->GetWidth() *
Float->Cos(Float->ToRadians(angle)) - @grip->GetWidth();
}
method : public : AddX(x : Int) ~ Nil {
@beam->AddX(x);
@grip_x_diff += x;
}
method : public : GetLeft() ~ Int {
return @grip->GetLeft();
}
method : public : GetRight() ~ Int {
return @grip->GetRight();
}
method : public : GetTop() ~ Int {
return @grip->GetTop();
}
method : public : GetBottom() ~ Int {
return @grip->GetBottom();
}
method : public : GetHeight() ~ Int {
return @grip->GetHeight();
}
method : public : GetWidth() ~ Int {
return @grip->GetWidth();
}
method : public : native : Update() ~ Nil {
if(@grip_x >= @beam_x_start & @grip_x <= @beam_x_end) {
@grip_x += @grip_dir;
}
else if(@grip_x > @beam_x_end) {
@grip_x := @beam_x_end;
@grip_dir := -1;
}
else if(@grip_x < @beam_x_start) {
@grip_x := @beam_x_start;
@grip_dir := 1;
};
@grip_y += @grip_dir * @grip_y_diff;
@grip->Set(@grip_x_diff + @grip_x, @grip_y);
}
method : public : Draw(frame_count : Int) ~ Nil {
@beam->Render();
@grip->Render();
}
method : public : Collides(rect : ImageSprite) ~ Bool {
return @grip->GetPosition()->Overlaps(rect->GetPosition());
}
}
class Gem implements Actor {
@rect : AnimatedImageSprite;
@hide : Bool;
New(x : Int, y : Int, rect : AnimatedImageSprite) {
@rect := rect;
@rect->Set(x, y);
}
method : public : Hide() ~ Nil {
@hide := true;
}
method : public : AddX(x : Int) ~ Nil {
@rect->AddX(x);
}
method : public : GetLeft() ~ Int {
return @rect->GetLeft();
}
method : public : GetRight() ~ Int {
return @rect->GetRight();
}
method : public : GetTop() ~ Int {
return @rect->GetTop();
}
method : public : GetBottom() ~ Int {
return @rect->GetBottom();
}
method : public : Draw(frame_count : Int) ~ Nil {
if(<>@hide) {
index := frame_count / 6 % @rect->GetClipCount();
@rect->Render(index);
};
}
method : public : Collides(rect : ImageSprite) ~ Bool {
if(@hide) {
return false;
}
else {
return @rect->GetPosition()->Overlaps(rect->GetPosition());
};
}
}
class Spider implements Actor {
@rect : ImageSprite;
@hide : Bool;
@base_y : Int;
@x_dir : Int;
@x_offset : Int;
@x_flip : Bool;
@y_dir : Int;
@y_offset : Int;
@game_over : Bool;
@current_level : Int;
New(x : Int, y : Int, rect : ImageSprite, hardness : Int) {
@rect := rect;
@base_y := y;
@current_level := Int->Random(3) + hardness;
@rect->Set(x, y);
@y_dir := -1;
@x_offset := Int->Random(20, 60);
}
method : public : GameOver() ~ Nil {
@game_over := true;
}
method : public : Hide() ~ Nil {
@hide := true;
}
method : public : AddX(x : Int) ~ Nil {
@rect->AddX(x);
}
method : public : GetLeft() ~ Int {
return @rect->GetLeft();
}
method : public : GetRight() ~ Int {
return @rect->GetRight();
}
method : public : GetTop() ~ Int {
return @rect->GetTop();
}
method : public : GetBottom() ~ Int {
return @rect->GetBottom();
}
method : public : native : Update() ~ Nil {
if(@x_dir <> 0) {
@x_offset += 1;
if(@x_offset > @current_level) {
@x_dir := 0;
@y_dir := -1;
@x_offset := 0;
};
}
else if(@rect->GetTop() < @base_y - (140 - @current_level) - @x_offset) {
@y_dir := 1;
}
else if(@rect->GetTop() >= @base_y) {
@y_dir := 0;
if(@x_flip) {
@x_dir := -1;
@x_flip := false;
}
else {
@x_dir := 1;
@x_flip := true;
};
};
@rect->AddX(@x_dir * 2);
@rect->AddY(@y_dir * 2);
}
method : public : Draw(frame_count : Int) ~ Nil {
if(<>@hide) {
@rect->Render();
};
}
method : public : Collides(rect : ImageSprite) ~ Bool {
if(@hide) {
return false;
}
else {
return @rect->GetPosition()->Overlaps(rect->GetPosition());
};
}
}
class SignPost implements Actor {
@rect : ImageSprite;
@hide : Bool;
New(x : Int, y : Int, rect : ImageSprite) {
@rect := rect;
@rect->Set(x, y);
}
method : public : Hide() ~ Nil {
@hide := true;
}
method : public : AddX(x : Int) ~ Nil {
@rect->AddX(x);
}
method : public : GetLeft() ~ Int {
return @rect->GetLeft();
}
method : public : GetRight() ~ Int {
return @rect->GetRight();
}
method : public : GetTop() ~ Int {
return @rect->GetTop();
}
method : public : GetBottom() ~ Int {
return @rect->GetBottom();
}
method : public : Draw(frame_count : Int) ~ Nil {
if(<>@hide) {
@rect->Render();
};
}
method : public : Collides(rect : ImageSprite) ~ Bool {
if(@hide) {
return false;
}
else {
return @rect->GetPosition()->Overlaps(rect->GetPosition());
};
}
}
consts GameConsts {
MAX_LEVELS := 5,
LEVEL_TIME := 45,
LEVEL_DEC_TIME := 2,
LEVEL_WIDTH_MAX := -1600,
TIME_POINTS := 3,
GEM_POINTS := 15,
PLAYER_BOX_MIN := 120,
PLAYER_BOX_MAX := 500,
SCREEN_WIDTH := 800,
SCREEN_HEIGHT := 600
}