Skip to content

Instantly share code, notes, and snippets.

@kdrnic
Created June 22, 2017 00:29
Show Gist options
  • Save kdrnic/0101c7d232e71aad35302d66d9ca543a to your computer and use it in GitHub Desktop.
Save kdrnic/0101c7d232e71aad35302d66d9ca543a to your computer and use it in GitHub Desktop.
Megaman in the blue mountains
// MIT License
//
// Copyright (c) 2012 kdrnic ( website: kdrnic.xyz )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <allegro.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <stddef.h>
#include <ctime>
#include <cmath>
struct TerrainPiece
{
int x, y;
int x2, y2;
};
struct Point
{
int x, y;
};
struct Spiny
{
float x;
int dx;
};
struct GorePiece
{
float x, y;
float speedX, speedY;
int offSetX, offSetY;
int sprite;
};
std::vector<TerrainPiece> terrain;
std::vector<Spiny> spinies;
std::vector<GorePiece> gorePieces;
ptrdiff_t myrandom(ptrdiff_t i)
{
return rand()%i;
}
unsigned int TimeInMilliseconds()
{
return (unsigned int)((double(std::clock()) / double(CLOCKS_PER_SEC)) * 1000.0);
}
void RANDAPOWAH(BITMAP *bmp)
{
int key = 'A' + 'r' + 'i' + 'e' + 'l' + ' ' + 'g' + 'o' + 's' + 't' + 'o' + 's' + 'a';
srand(key);
std::vector<Point> points;
for(int x = 0; x < bmp->w; x++)
{
for(int y = 0; y < bmp->h; y++)
{
Point p;
p.x = x;
p.y = y;
points.push_back(p);
}
}
ptrdiff_t (*p_myrandom)(ptrdiff_t) = myrandom;
std::random_shuffle(points.begin(), points.end(), p_myrandom);
BITMAP *temp = create_bitmap(bmp->w, bmp->h);
int i = 0;
for(int x = 0; x < bmp->w; x++)
{
for(int y = 0; y < bmp->h; y++)
{
putpixel(temp, x, y, getpixel(bmp, points[i].x, points[i].y));
i++;
}
}
draw_sprite(bmp, temp, 0, 0);
destroy_bitmap(temp);
}
void REVERSALRANDAPOWAH(BITMAP *bmp)
{
int key = 'A' + 'r' + 'i' + 'e' + 'l' + ' ' + 'g' + 'o' + 's' + 't' + 'o' + 's' + 'a';
srand(key);
std::vector<Point> points;
for(int x = 0; x < bmp->w; x++)
{
for(int y = 0; y < bmp->h; y++)
{
Point p;
p.x = x;
p.y = y;
points.push_back(p);
}
}
ptrdiff_t (*p_myrandom)(ptrdiff_t) = myrandom;
std::random_shuffle(points.begin(), points.end(), p_myrandom);
BITMAP *temp = create_bitmap(bmp->w, bmp->h);
int i = 0;
for(int x = 0; x < bmp->w; x++)
{
for(int y = 0; y < bmp->h; y++)
{
putpixel(temp, points[i].x, points[i].y, getpixel(bmp, x, y));
i++;
}
}
draw_sprite(bmp, temp, 0, 0);
destroy_bitmap(temp);
}
void InitTerrain()
{
TerrainPiece p;
p.x = 0;
p.y = 211;
p.x2 = 22;
p.y2 = 189;
terrain.push_back(p);
p.x = 22;
p.y = 189;
p.x2 = 75;
p.y2 = 150;
terrain.push_back(p);
p.x = 75;
p.y = 150;
p.x2 = 97;
p.y2 = 137;
terrain.push_back(p);
p.x = 97;
p.y = 137;
p.x2 = 122;
p.y2 = 126;
terrain.push_back(p);
p.x = 122;
p.y = 126;
p.x2 = 145;
p.y2 = 121;
terrain.push_back(p);
p.x = 145;
p.y = 121;
p.x2 = 168;
p.y2 = 121;
terrain.push_back(p);
p.x = 168;
p.y = 121;
p.x2 = 199;
p.y2 = 129;
terrain.push_back(p);
p.x = 199;
p.y = 129;
p.x2 = 228;
p.y2 = 146;
terrain.push_back(p);
p.x = 228;
p.y = 146;
p.x2 = 247;
p.y2 = 149;
terrain.push_back(p);
p.x = 247;
p.y = 149;
p.x2 = 271;
p.y2 = 163;
terrain.push_back(p);
p.x = 271;
p.y = 163;
p.x2 = 302;
p.y2 = 188;
terrain.push_back(p);
p.x = 302;
p.y = 188;
p.x2 = 313;
p.y2 = 201;
terrain.push_back(p);
p.x = 313;
p.y = 201;
p.x2 = 330;
p.y2 = 188;
terrain.push_back(p);
p.x = 330;
p.y = 188;
p.x2 = 396;
p.y2 = 151;
terrain.push_back(p);
p.x = 396;
p.y = 151;
p.x2 = 455;
p.y2 = 101;
terrain.push_back(p);
p.x = 455;
p.y = 101;
p.x2 = 485;
p.y2 = 83;
terrain.push_back(p);
p.x = 485;
p.y = 83;
p.x2 = 564;
p.y2 = 64;
terrain.push_back(p);
p.x = 564;
p.y = 64;
p.x2 = 594;
p.y2 = 51;
terrain.push_back(p);
p.x = 594;
p.y = 51;
p.x2 = 621;
p.y2 = 28;
terrain.push_back(p);
p.x = 621;
p.y = 28;
p.x2 = 640;
p.y2 = 85;
terrain.push_back(p);
}
float TerrainHeight(float x)
{
if(x <= 0) return 211;
if(x >= 640) return 85;
int i;
for(i = 0; i < terrain.size(); i++)
{
if(x > terrain[i].x && x <= terrain[i].x2) break;
}
float tWidth = terrain[i].x2 - terrain[i].x;
float tHeight = terrain[i].y2 - terrain[i].y;
float rX = x - float(terrain[i].x);
float fRY = float(tHeight) * (float(rX) / float(tWidth));
return float(terrain[i].y) + fRY;
}
void InitSpinies()
{
Spiny p;
for(int x = 30; x < 318; x++)
{
// Calculated to give a spiny for each 21.2 pixels on average
if(rand() % 1000000 > 47619) continue;
/* (rand() % 2) -> 0 ou 1
* ((rand() % 2) + 1) -> 1 ou 2
* (((rand() % 2) + 1) & 2) -> 0 ou 2
* 1 - (((rand() % 2) + 1) & 2) -> -1 ou 1
*/
p.dx = 1 - (((rand() % 2) + 1) & 2);
p.x = x;
spinies.push_back(p);
}
}
void UpdateSpinies()
{
for(int i = 0; i < spinies.size(); i++)
{
// Calculated to reverse direction each 4 seconds on average
if(rand() % (60 * 4) == 0) spinies[i].dx *= -1;
if(spinies[i].x <= 0) spinies[i].dx = 1;
if(spinies[i].x >= 318) spinies[i].dx = -1;
spinies[i].x += (spinies[i].dx * 0.35);
}
}
void UpdateGore()
{
const int approxX = 1;
const float elasticity = 0.5;
for(int i = 0; i < gorePieces.size(); i++)
{
gorePieces[i].x += gorePieces[i].speedX;
gorePieces[i].y += gorePieces[i].speedY;
gorePieces[i].speedY += 0.075;
if(gorePieces[i].x < 0)
{
gorePieces[i].x = 0;
if(gorePieces[i].speedX < 0) gorePieces[i].speedX *= -1;
}
if(gorePieces[i].x > 640)
{
gorePieces[i].x = 640;
if(gorePieces[i].speedX > 0) gorePieces[i].speedX *= -1;
}
if(gorePieces[i].y > TerrainHeight(gorePieces[i].x))
{
// Get a unit vector parallel to the terrain
float px, py;
px = approxX;
py = TerrainHeight(gorePieces[i].x + approxX) - TerrainHeight(gorePieces[i].x);
float pl = std::sqrt((px * px) + (py * py));
px /= pl;
py /= pl;
// Project velocity over the parallel vector
float p = px * gorePieces[i].speedX + py * gorePieces[i].speedY;
// Split speed in components parallel and perpendicular to the terrain vector
float cx, cy, cpx, cpy;
cx = px * p;
cy = py * p;
cpx = gorePieces[i].speedX - cx;
cpy = gorePieces[i].speedY - cy;
// Use those to calculate new speed
gorePieces[i].speedX = cx - cpx;
gorePieces[i].speedY = cy - cpy;
// Apply elasticity so it's not an unrealistic perfectly ellastical collision
gorePieces[i].speedX *= elasticity;
gorePieces[i].speedY *= elasticity;
// Move the piece up, to dont fuck up on the next update
gorePieces[i].y = TerrainHeight(gorePieces[i].x);
}
}
}
void InitGore(float x, float y, float scaleX, float scaleY)
{
int sX[6];
int sY[6];
int centerX = 1;
int centerY = -12;
sX[0] = 2;
sY[0] = -16;
sX[1] = -8;
sY[1] = -18;
sX[2] = 7;
sY[2] = -17;
sX[3] = 1;
sY[3] = -8;
sX[4] = 5;
sY[4] = -8;
sX[5] = -5;
sY[5] = -8;
int oX[6];
int oY[6];
oX[0] = 8;
oY[0] = 22;
oX[1] = 8;
oY[1] = 2;
oX[2] = 5;
oY[2] = 4;
oX[3] = 7;
oY[3] = 12;
oX[4] = 4;
oY[4] = 2;
oX[5] = 11;
oY[5] = 2;
for(int i = 0; i < 6; i++)
{
GorePiece p;
p.sprite = i;
p.x = x + float(sX[i]) * scaleX;
p.y = y + float(sY[i]) * scaleY;
p.offSetX = oX[i];
p.offSetY = oY[i];
p.speedX = (sX[i] - centerX) / std::sqrt(((sX[i] - centerX) * (sX[i] - centerX)) + ((sY[i] - centerY) * (sY[i] - centerY)));
p.speedY = (sY[i] - centerY) / std::sqrt(((sX[i] - centerX) * (sX[i] - centerX)) + ((sY[i] - centerY) * (sY[i] - centerY)));
p.speedX;
p.speedY;
gorePieces.push_back(p);
}
}
void Rest(unsigned int time)
{
unsigned int start = TimeInMilliseconds();
while(TimeInMilliseconds() - start < time) continue;
}
int main(int argc, char **argv)
{
allegro_init();
set_color_depth(32);
set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);
BITMAP *db = create_bitmap(640, 480);
BITMAP *bg = load_bitmap("bg.bmp", 0);
BITMAP *megaSprite = load_bitmap("megaman.bmp", 0);
BITMAP *spinySprite = load_bitmap("spiny.bmp", 0);
BITMAP *goreSprites[6];
goreSprites[0] = load_bitmap("gore1.bmp", 0);
goreSprites[1] = load_bitmap("gore2.bmp", 0);
goreSprites[2] = load_bitmap("gore3.bmp", 0);
goreSprites[3] = load_bitmap("gore4.bmp", 0);
goreSprites[4] = load_bitmap("gore5.bmp", 0);
goreSprites[5] = load_bitmap("gore6.bmp", 0);
REVERSALRANDAPOWAH(bg);
if(argc == 2) save_bitmap(argv[1], bg, 0);
install_keyboard();
install_timer();
InitTerrain();
srand(std::time(0));
InitSpinies();
int x1, y1, x2, y2;
x1 = 1;
y1 = 153;
x2 = 65;
y2 = 153 + 48;
float megaX, megaY;
megaX = 10;
bool jumping = false;
bool up = false;
float megaSpeedY = 0;
float gravity = 0.075;
float jumpSpeed = -1.5;
int megaFrame;
int megaFPS;
BITMAP *temp = create_bitmap(41, 37);
bool flip;
bool megaDeath = false; // LOOOOOOL
bool win = false;
BITMAP *lastDb = create_bitmap(320, 240);
while(!key[KEY_ESC])
{
float oldX = megaX;
unsigned int frameStart = TimeInMilliseconds();
if(!megaDeath)
{
if(key[KEY_LEFT] && (megaX > 0)) megaX -= 0.5;
if(key[KEY_LEFT] && (megaX > 0)) flip = true;
if(key[KEY_RIGHT] && (megaX < 640)) megaX += 0.5;
if(key[KEY_RIGHT] && (megaX < 640)) flip = false;
}
float th = TerrainHeight(megaX);
if(!megaDeath)
{
if((key[KEY_UP]) && (!up) && (jumping == false))
{
megaSpeedY = jumpSpeed;
jumping = true;
}
}
up = key[KEY_UP];
if(jumping)
{
megaY += megaSpeedY;
megaSpeedY += gravity;
}
else
{
megaY = th;
}
if(th <= megaY)
{
jumping = false;
megaY = th;
}
UpdateSpinies();
UpdateGore();
int megaDrawX, megaDrawY;
megaDrawX = int(640.0 * ((megaX - float(x1)) / float(x2 - x1)));
megaDrawY = int(480.0 * ((megaY - float(y1)) / float(y2 - y1)));
if(oldX == megaX)
{
blit(megaSprite, temp, 0, 0, 0, 0, 41, 37);
}
else
{
blit(megaSprite, temp, 0, 37 + (((megaFrame / 3) % 4) * 37), 0, 0, 41, 37);
}
stretch_blit(bg, db, x1, y1, (x2 - x1), (y2 - y1), 0, 0, 640, 480);
for(int i = 0; i < spinies.size(); i++)
{
int spDrawX = int(640.0 * ((float(spinies[i].x) - float(x1)) / float(x2 - x1)));
if(spDrawX < -17) continue;
if(spDrawX > 657) continue;
int spDrawY = int(480.0 * ((TerrainHeight(spinies[i].x) - float(y1)) / float(y2 - y1)));
if(spinies[i].dx < 0) draw_sprite(db, spinySprite, spDrawX - 17, spDrawY - 32);
if(spinies[i].dx > 0) draw_sprite_h_flip(db, spinySprite, spDrawX - 17, spDrawY - 32);
if(megaDeath) continue;
if(spDrawX - 17 > megaDrawX + 8) continue;
if(spDrawX + 17 < megaDrawX - 8) continue;
if(megaDrawY < spDrawY - 16) continue;
megaDeath = true;
InitGore(megaX, megaY, float(x2 - x1) / 640.0, float(y2 - y1) / 480.0);
}
if(!megaDeath)
{
if(flip) draw_sprite_h_flip(db, temp, megaDrawX - 21, megaDrawY - 36);
else draw_sprite(db, temp, megaDrawX - 21, megaDrawY - 36);
}
for(int i = 0; i < gorePieces.size(); i++)
{
int gDrawX = int(640.0 * ((gorePieces[i].x - float(x1)) / float(x2 - x1)));
int gDrawY = int(480.0 * ((gorePieces[i].y - float(y1)) / float(y2 - y1)));
draw_sprite(db, goreSprites[gorePieces[i].sprite], gDrawX - gorePieces[i].offSetX, gDrawY - gorePieces[i].offSetY);
}
draw_sprite(screen, db, 0, 0);
megaFrame++;
if(megaDrawX < 213)
{
if(x1 >= 2)
{
x1--;
x2--;
}
}
if(megaDrawX > 437)
{
if(x2 <= 637)
{
x1++;
x2++;
}
}
if(megaDrawY < 160)
{
if(y1 >= 2)
{
y1--;
y2--;
}
}
if(megaDrawY > 320)
{
if(y2 <= 637)
{
y1++;
y2++;
}
}
if(megaX > 318)
{
win = true;
break;
}
if(TimeInMilliseconds() - frameStart < 17) rest(17 - TimeInMilliseconds() + frameStart);
}
stretch_blit(db, lastDb, 0, 0, 640, 480, 0, 0, 320, 240);
float _x1, _x2, _y1, _y2;
_x1 = x1;
_x2 = x2;
_y1 = y1;
_y2 = y2;
float d_x1, d_x2, d_y1, d_y2;
d_x1 = (0.0 - x1) / 30;
d_x2 = (640.0 - x2) / 30;
d_y1 = (0.0 - y1) / 30;
d_y2 = (480.0 - y2) / 30;
if(win)
{
bool dontFuck = false;
while(!key[KEY_ESC])
{
int frameStart = TimeInMilliseconds();
if(_x1 > 0) _x1 += d_x1;
if(_x2 < 640) _x2 += d_x2;
if(_y1 > 0) _y1 += d_y1;
if(_y2 < 640) _y2 += d_y2;
if(_x1 < 0) _x1 = 0;
if(_x2 > 640) _x1 = 640;
if(_y1 < 0) _y1 = 0;
if(_y2 > 480) _y2 = 480;
float __x1, __x2, __y1, __y2;
if((_x1 >= 1) && (_x2 <= 639.0) && (_y1 >= 1) && (_y2 <= 478.0) && (!dontFuck))
{
__x1 = int(640.0 * ((float(x1) - _x1) / (_x2 - _x1)));
__x2 = int(640.0 * ((float(x2) - _x1) / (_x2 - _x1)));
__y1 = int(480.0 * ((float(y1) - _y1) / (_y2 - _y1)));
__y2 = int(480.0 * ((float(y2) - _y1) / (_y2 - _y1)));
stretch_blit(bg, db, int(_x1), int(_y1), int(_x2 - _x1), int(_y2 - _y1), 0, 0, 640, 480);
stretch_blit(lastDb, db, 0, 0, 320, 240, int(__x1), int(__y1), int(__x2 - __x1), int(__y2 - __y1));
}
else
{
draw_sprite(db, bg, 0, 0);
stretch_blit(lastDb, db, 0, 0, 320, 240, x1, y1, x2 - x1, y2 - y1);
dontFuck = true;
}
draw_sprite(screen, db, 0, 0);
int delay = 17 - (TimeInMilliseconds() - frameStart);
if(delay > 0) Rest(delay);
}
}
allegro_exit();
}
END_OF_MAIN();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment