/*
* Remigiusz Jan Andrzej Modrzejewski, http://lrem.net/ <lrem at go2.pl>
* Distributed under the GPL, for more details see:
* http://lrem.net/zracer.xhtml
*
* ZRacer - a simple arcade game in ncurses
*
* ZRacer is a racing game where 1 - 2 players race on a randomly
* generated racecourse with split-screen and using the same keyboard.
*
* Conventions taken:
* - coordinates order is (y, x)
* - (0, 0) is upper left corner of everything
* - as a result, finish line is at line 0
* - car doesn't take up the whole rectangle (for collision checking)
* - when a car crashes, it's window is frozen and no input is taken
* - the track is stored as its ascii-art representation
*/
#include <curses.h>
#include <cstdarg>
#include <cstdio>
#include <string>
#include <vector>
#include <ctime>
#include <cassert>
#include <cstdlib>
using namespace std;
// This one defines a random function with results in range 0..1 (double).
#define drand()((double)rand()/RAND_MAX)
// Various constants
#define MAX_CAR_SIZE 20
#define MAX_PLAYERS 2
#define INF 123456789
#define KEY_ESC 27 // Missing in ncurses...
#define RESULTS_COLORS 11
#define MESSAGE_LENGTH 100
// Action values
#define ACCELERATE -1
#define BRAKE 1
#define LEFT -1
#define RIGHT 1
// Make passage, but don't exceed available space.
#define MINIMAL_WIDTH\
(min(settings.players*settings.car_size*2.5, (double)settings.race_width-2))
// Main menu item defines, for convenience.
#define MENU_QUIT 0
#define MENU_START 1
#define MENU_OPTIONS 2
/*
* In-game settings. This needn't really by a struct, but it looks more
* readable to access settings by "settings.delay" than "delay".
*/
struct _settings
{
// Basic game delay, is not equal to move time, but is a factor.
timespec delay;
// The axis of splitscreen.
bool vertical_split;
// Whether both players race on the same-looking racecourse.
bool similar_track;
// Or maybe literally the same one? (Collisions possible)
bool shared_track;
// The sizes of the course, better make it bigger than the car ;)
int race_length, race_width;
// The minimal width of the road the players can drive.
int minimal_width;
// The number of participants.
int players;
// Character with which the cars are drawn.
char character;
// The size of the car.
int car_size;
// The distance interval at which the speed of the car changes.
int speed_base;
// The chance of generating a rock on a given line
double rock_chance;
// The chance of generating a turning on a given line
double turn_chance;
// Keys players use to interact with the game.
int controls[MAX_PLAYERS][4];
void reset(void)
{
delay.tv_sec = 0;
// Hundredth of a second * const.
delay.tv_nsec = 1000000*25;
similar_track = vertical_split = true;
shared_track = true;
race_length = 500;
// Zero makes these 2 variables adjusted to screen size. race_width = 0; minimal_width = 0;
players = 1;
character = '^';
car_size = 10;
speed_base = 5;
rock_chance = 0.025;
turn_chance = 0.125;
// Arrow keys for first player
controls[0][0]=KEY_UP;
controls[0][1]=KEY_DOWN;
controls[0][2]=KEY_LEFT;
controls[0][3]=KEY_RIGHT;
// WSAD for second
controls[1][0]='w';
controls[1][1]='s';
controls[1][2]='a';
controls[1][3]='d';
}
void editor(void);
void _edit_players(void);
void _edit_length(void);
void _edit_width(void);
void _edit_rocks(void);
void _edit_turns(void);
void _edit_delay(void);
void _edit_sharing(void);
} settings;
class car_image
{
/*
* This is where we store the image of the car.
* For performance purposes, it is created only once when scaled.
*/
bool storage [MAX_CAR_SIZE+1][MAX_CAR_SIZE+1];
// And as list of used pixels coords.
vector<pair<int, int> > dots;
char character;
int color, size;
/*
* Internal functions. First one is _clear() - it just sets all
* the storage area to false. Second one is line() - takes coords
* of 2 points and draws a line between them (or more literally
* just marks certain values within storage as true).
*/
void _clear(void);
void _line(int, int, int, int);
public:
/*
* Constructor, takes input from the settings.
*/
car_image(void);
/*
* This one simply draws the car on the given window. It assumes
* that this can clearly be done, so all the checks need to be
* done earlier. The parameters it takes are the window and position
* of upper left corner of the car.
*/
void display(WINDOW*, int, int);
/*
* Draws the explosion of the car. Parameters are windows, position
* of upper left corner of the car. Position is relative to the window,
* _not_ the track.
*/
void explode(WINDOW*, int, int);
/*
* Collision happens only when an obstacle is on a pace taken by the
* car. It is possible to have the obstacle between the car's "ribs".
* This checks whether given pace relative to *car's position* is
* taken by the car.
*/
bool collision_check(int, int);
/*
* These are simple mutators.
*/
void set_character(char);
void set_color(int);
// And a simple accessor.
vector<pair<int, int> > get_dots(void);
};
class track
{
/*
* These are the most important data for the game. The track should
* be generated only once each game, preferably shared between players.
*/
vector<vector<char> > circuit;
public:
/*
* Two constructors. One takes input from the settings, second just
* copies another track.
*/
track(void);
track(track*);
/*
* Takes the number of the top line to display and the window.
* Window's height and width are grabbed by getmaxyx().
* There's an assertion that the window is wide enough.
*/
void display(WINDOW*, int);
/*
* Tells whether there's an obstacle at a given pace.
*/
bool taken(int, int);
void mark(int, int, car_image*);
void unmark(int, int, car_image*);
};
class player_handler
{
WINDOW* screen;
track* course;
car_image* car;
// last_move is the time value of the previous player's action
// (y, x) is the position of the upper left corner of the car
// top_line is the top displayed line of the course
// commands store what player clicked
int last_move, y, x, top_line, command_x, command_y, screen_height;
int controls[4];
public:
/*
* This constructor prepares the part of the screen for the player.
* It decides which part of screen to take, and the color of the car,
* taking the settings and the player number. The track is generated
* outside it.
*/
player_handler(int, track*);
/*
* Player's main loop. Returns true if the player continues to play,
* and false if the game ends for him. Also does redraw the window.
*/
bool tick(int);
/*
* Knowing the player's key controls, this one does what it's named for.
* Only sets the action to perform during next move.
*/
void parse_input(int);
/*
* These are used only in shared track races in order to catch player-player
* collisions.
*/
void mark_position(void);
void unmark_position(void);
/*
* Apart from deallocating standard structures, this one has also to
* destroy the ncurses window it created, so it needs a separate destructor.
*/
//~player_handler(void);
};
class game
{
int time;
player_handler* players[MAX_PLAYERS];
bool alive[MAX_PLAYERS];
public:
/*
* Constructor does all the fancy things like initializing ncurses,
* while destructor brings back normal tty behaviour. This is great,
* as it doesn't force me to remember about deinitialization any
* time I want to break the game.
*/
game(void);
~game(void);
/*
* This is the game's main loop action...
* It returns true as long as game continues.
*/
bool tick(void);
};
int main_menu (void);
// This is a wrapper around printw, also accepts arbitrary number of arguments
void message (char*, ...);
int main (void)
{
bool keep_asking = true;
settings.reset();
while(keep_asking)
{
switch(main_menu())
{
case MENU_START:
{
game race;
while(race.tick())
nanosleep(&settings.delay, NULL);
break;
}
case MENU_OPTIONS:
settings.editor();
break;
case MENU_QUIT:
keep_asking = false;
}
}
// In C a "return 0;" would come here, but this is not C...
}
/*
* This function opens a window with a message disregarding anything else that was
* running. Good for displaying error messages, final results and so. Waits for an
* ESC pressed before quiting.
*/
void message (char* format_string, ...)
{
va_list args;
// Allocate a buffer for the message.
char* final_string = new char[MESSAGE_LENGTH];
// Fetch the "..." arguments.
va_start(args, format_string);
// Transform all the arguments into a single string.
vsprintf(final_string, format_string, args);
// Finalize the work on the "..." arguments.
va_end(args);
// Get screen resolution.
int screen_height, screen_width;
getmaxyx(stdscr, screen_height, screen_width);
// Create a window for the message, of it's size
WINDOW* message_win = newwin(1, strlen(final_string),
// and at the centre of the screen.
screen_height/2, screen_width/2 - strlen(final_string)/2);
// Print the message.
wattron(message_win, COLOR_PAIR(RESULTS_COLORS));
wattron(message_win, A_BOLD);
waddstr(message_win, final_string);
wattroff(message_win, COLOR_PAIR(RESULTS_COLORS));
wattroff(message_win, A_BOLD);
wrefresh(message_win);
// Wait for an ESC.
while(getch()!=KEY_ESC)
nanosleep(&settings.delay, NULL);
// Clean up after myself.
delwin(message_win);
}
/*
* This class is created solely for the cool trick used in game constructor/destructor.
* It's struct just because it doesn't have anything private.
* Watch it's ingenuity and simplicity!
*/
struct simple_curses
{
simple_curses(void)
{
// Initialize ncurses. This is done separately from initializing for
// the game in order to have different options.
initscr();
cbreak();
clear();
}
~simple_curses(void)
{
// Fall back to normal options
nocbreak();
endwin();
}
};
int main_menu (void)
{
// Whole trick is: constructor get's run here, destructor whenever leaving
// the function.
simple_curses enviroment;
// Tell them what to do...
printw("\t\tWelcome to ZRacer by lRem!\n");
printw("If you like this game look for more at http://lrem.net/\n\n");
printw("\t\t\tMAIN MENU:\n\n");
printw("q) Quit the game.\n");
printw("s) Start a new game.\n");
printw("o) Options.\n\n");
// And wait for them do it.
char pressed = 0;
for(;;)
{
printw("Choose any option: ");
refresh();
pressed = getch();
switch(pressed)
{
case 'q':
return MENU_QUIT;
// No breaks - return already quits the switch.
case 's':
return MENU_START;
case 'o':
return MENU_OPTIONS; }
// In case user missed the key, print the next request in next line.
addch('\n');
}
}
void _settings::editor(void)
{
simple_curses enviroment;
printw("\t\tSETTINGS EDITOR\n"); printw("q) Quit the editor\n");
printw("p) Set the number of players\n");
printw("l) Set the length of the racecourse\n");
printw("r) Set the chance to generate a rock\n");
printw("t) Set the chance to generate a turning\n");
printw("s) Set master delay\n");
printw("h) Set track sharing\n");
// This doesn't give nice results...
//printw("w) Set the width of the racecourse\n");
char pressed = 0;
for(;;)
{ printw("Choose any option: ");
refresh();
pressed = getch();
switch(pressed)
{
case 'q':
return;
case 'p':
_edit_players();
break;
case 'l': _edit_length();
break;
case 'r':
_edit_rocks();
break;
case 't':
_edit_turns();
break;
case 's':
_edit_delay();
break; case 'h':
_edit_sharing();
//case 'w':
// _edit_width();
// break;
}
}
}
void _settings::_edit_players(void) {
printw("\n\tSelect the number of players (1 - %d, currently %d):", MAX_PLAYERS, players); players = 0;
// While players outside the possible range.
for(; players<1 || MAX_PLAYERS<players;)
{
players = getch() - '0';
addch(' ');
}
addch('\n');
}
void _settings::_edit_length(void)
{
printw("\n\tSet the length of the track (arbitrary, currently %d):", race_length);
race_length = -1;
// While players outside the possible range.
for(; race_length<0;)
{
scanw("%d", &race_length);
}
}
void _settings::_edit_width(void)
{
printw("\n\tSet the width of the track (narrower than terminal, 0 means max, currently %d):", race_width);
race_width = -1;
// While players outside the possible range.
for(; race_width<0;)
{
scanw("%d", &race_width);
}
}
void _settings::_edit_rocks(void)
{
printw("\n\tSet the chance of generating a rock (0-1, currently %lf):", rock_chance);
rock_chance = -1;
for(; rock_chance<0 || 1<rock_chance;)
{
scanw("%lf", &rock_chance);
}
}
void _settings::_edit_turns(void)
{
printw("\n\tSet the chance of generating a turn (0-1, currently %lf):", turn_chance);
turn_chance = -1;
for(; turn_chance<0 || 1<turn_chance;)
{
scanw("%lf", &turn_chance);
}
}
void _settings::_edit_delay(void)
{
printw("\n\tSet the master delay (positive, nanosceonds, currently %d):", delay.tv_nsec);
delay.tv_nsec = -1;
for(; delay.tv_nsec<0;)
{
scanw("%d", &delay.tv_nsec);
}
}
void _settings::_edit_sharing(void)
{
printw("\n\tShould the track be Similar, sHared or Different for different palyers? ");
char response;
for(; response!='s' && response!='h' && response!='d';)
{
scanw("%c", &response);
switch(tolower(response))
{
case 's':
similar_track = true;
shared_track = false;
break;
case 'h':
similar_track = true;
shared_track = true;
break;
case 'd':
similar_track = false;
shared_track = false;
break;
}
}
}
bool game::tick(void)
{
bool game_continues = false;
time++;
// For every key waiting in buffer...
int pressed_key;
while((pressed_key = getch()) != ERR)
{
if(pressed_key == KEY_ESC) // End the game.
for(int i = 0; i<settings.players; i++)
alive[i]=false; // By killing all players.
// Pass the input to each player.
for(int i = 0; i<settings.players; i++)
players[i]->parse_input(pressed_key);
}
// Checking for player-player collisions is realized by marking each player's position
// as an obstacle on the track.
if(settings.shared_track)
for(int i=0; i<settings.players; i++)
if(alive[i])
players[i]->mark_position();
for(int i=0; i<settings.players; i++)
if(alive[i])
if(settings.shared_track)
{
players[i]->unmark_position();
game_continues = (alive[i] = players[i]->tick(time)) || game_continues;
players[i]->mark_position();
}
else
// If the player dies it sets his alive status to false.
// If he lives, then there is a reason to continue the game.
game_continues = (alive[i] = players[i]->tick(time)) || game_continues;
if(settings.shared_track)
for(int i=0; i<settings.players; i++)
players[i]->unmark_position();
// Results.
if(!game_continues)
message("Game finished after %d turns.", time);
return game_continues;
}
game::game (void)
{
// Initialize the RNG
// We have already a variable called "time", and we need a function of the same name.
// In order to circumvent this problem, we use the namespaces.
srand(std::time(NULL));
// Initialize ncurses.
initscr();
// Initialize colors (refuse to start without them).
assert(has_colors());
start_color();
keypad(stdscr, TRUE);
cbreak();
noecho();
nonl();
nodelay(stdscr, true);
// Color palette.
init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK); init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
init_pair(RESULTS_COLORS, COLOR_YELLOW, COLOR_BLUE);
// Adjust settings, if needed.
if(settings.race_width == 0)
{
int y, x;
getmaxyx(stdscr, y, x);
settings.race_width = settings.vertical_split ? x/settings.players : x;
}
if(settings.minimal_width == 0)
settings.minimal_width = (int)(MINIMAL_WIDTH);
// Prepare players
if(settings.shared_track)
{
track* course = new track();
for(int i = 0; i<settings.players; i++)
players[i]=new player_handler(i, course);
}
else
if(settings.similar_track)
{
track* course = new track();
for(int i = 0; i<settings.players; i++)
players[i]=new player_handler(i, course);
}
else
{
for(int i = 0; i<settings.players; i++)
players[i]=new player_handler(i, new track());
}
for(int i = 0; i<settings.players; i++)
alive[i]=true;
// Let the moves begin.
time = 0; }
game::~game (void)
{ echo();
nl();
nocbreak();
nodelay(stdscr, false);
endwin();
}
track::track (void)
{
// Allocate the structures.
circuit.resize(settings.race_length); for(vector<vector<char> >::iterator it = circuit.begin(); it!=circuit.end(); it++)
it->resize(settings.race_width);
// And create the course!
int borders[2];
int borders_directions[2]={0,0};
// Initially make the road halfway between minimal and maximal possible.
assert(settings.minimal_width <= settings.race_width);
borders[0] = (settings.race_width - settings.minimal_width)/4;
borders[1] = (settings.race_width*3 + settings.minimal_width)/4;
// And generate the lines.
for(int i=settings.race_length-1; 0<=i; i--)
{
// Put some background.
for(int j=0; j<(int)circuit[i].size(); j++)
circuit[i][j]=' ';
// Distance meter.
circuit[i][0]='0'+i%10;
// Occasional rock on the track :>
if(drand()<settings.rock_chance)
circuit[i][rand()%settings.race_width]='*';
// Move the kerbs.
borders[0]+=borders_directions[0];
borders[1]+=borders_directions[1];
// Draw the kerbs.
switch(borders_directions[0])
{// Different chars, depending on the kerb direction.
case 0:
circuit[i][borders[0]]='|';
break;
case 1:
circuit[i][borders[0]]='/';
break;
case -1:
circuit[i][borders[0]]='\\';
}
switch(borders_directions[1])
{
case 0:
circuit[i][borders[1]]='|';
break;
case 1:
circuit[i][borders[1]]='/';
break;
case -1:
circuit[i][borders[1]]='\\';
}
// Turn the kerbs...
int tries = 0; // This is in case it gets to narrow and no space at once (hangs).
while(
tries++<5 &&
(drand()<settings.turn_chance || // If RNG wants so,
borders[0]+borders_directions[0] == 0 // or no space.
))
borders_directions[0] = rand()%3-1;
if(borders[1]-borders[0] < settings.minimal_width) // If to narrow,
borders_directions[0] = -1; // Make it wider
// A sanity check.
if(borders[0]+borders_directions[0] == 0)
borders_directions[0] = 0;
// And the second one.
tries = 0;
while(
tries++<5 &&
(drand()<settings.turn_chance || // If RNG wants so,
borders[1]+borders_directions[1] == settings.race_width // or no space.
))
borders_directions[1]=rand()%3-1;
if(borders[1]-borders[0] < settings.minimal_width)
borders_directions[1] = 1;
if(borders[1]+borders_directions[1] == settings.race_width)
borders_directions[1] = 0;
}
}
void track::display(WINDOW* screen, int top_line)
{
// Get the geometry.
int screen_width, screen_height;
getmaxyx(screen, screen_height, screen_width);
// For every visible line...
for(int i = top_line; i<top_line+screen_height; i++)
{ // Move the cursor at it's beginning...
// Position at screen centre.
wmove(screen, i-top_line, (screen_width-settings.race_width)/2);
// And print all the characters.
for(int j=0; j<settings.race_width; j++)
waddch(screen, circuit[i][j]);
}
}
bool track::taken(int y, int x)
{
return circuit[y][x]!=' ';
}
void track::mark(int y, int x, car_image *car)
{
vector<pair<int, int> > dots = car->get_dots();
for(unsigned int i=0; i<dots.size(); i++)
if(circuit[y+dots[i].first][x+dots[i].second] == ' ')
circuit[y+dots[i].first][x+dots[i].second] = settings.character;
}
void track::unmark(int y, int x, car_image *car)
{
vector<pair<int, int> > dots = car->get_dots();
for(unsigned int i=0; i<dots.size(); i++)
if(circuit[y+dots[i].first][x+dots[i].second] == settings.character)
circuit[y+dots[i].first][x+dots[i].second] = ' ';
}
player_handler::player_handler(int position, track* racecourse)
{
// Set the sizes for the windows.
// Height gets reused and thus is declared within class.
int screen_width;
getmaxyx(stdscr, screen_height, screen_width);
int width = settings.vertical_split? screen_width/settings.players : screen_width;
int height = settings.vertical_split? screen_height : screen_height/settings.players;
// We can't set the track to be wider than the display.
assert(settings.race_width<=width);
// Prepare screen part.
if(settings.vertical_split)
{
screen = newwin(
height, width,
// Take the corresponding vertical stripe.
0, width*(settings.players - position - 1)
);
}
else
{
screen = newwin(
height, width,
// Take the corresponding horizontal stripe.
height*position, 0
);
}
// Just copy this pointer.
course = racecourse;
// And create an image for yourself
car = new car_image();
// Place the car at a reasonable place.
y = settings.race_length - settings.car_size;
if(settings.shared_track)
x = (settings.race_width - (settings.car_size+1)*(settings.players-2*position))/ 2;
else
x = settings.race_width/2;
// We want the car at the very bottom of the screen.
top_line = settings.race_length - height;
// If not set, the player actually could freeze for a while.
last_move = -INF;
// Copy the controls.
memcpy(controls, settings.controls[position], 4*sizeof(int));
// And make sure player doesn't take off.
command_y = command_x = 0;
}
/*player_handler::~player_handler(void)
{
// It's so simple...
delwin(screen);
}*/
// Just a simple switched assignment.
void player_handler::parse_input(int pressed_key) {
if(pressed_key == controls[0]) command_y=ACCELERATE; if(pressed_key == controls[1])
command_y=BRAKE; if(pressed_key == controls[2]) command_x=LEFT;
if(pressed_key == controls[3]) command_x=RIGHT; }
void player_handler::mark_position(void)
{
course->mark(y, x, car);
}
void player_handler::unmark_position(void)
{
course->unmark(y, x, car);
}
bool player_handler::tick(int time)
{
bool survive = true;
// The higher the car on the screen, the faster it moves.
if(last_move + (y-top_line)/settings.speed_base < time)
{
last_move=time;
y--;
// Watch to not segfault here.
top_line=max(0, top_line-1);
// Handle commands
y+=command_y;
x+=command_x;
command_y = command_x = 0;
// Make sure he doesn't escape from the screen.
y = max(top_line, min(top_line + screen_height - settings.car_size, y));
if(y <= 0) // Plain win
return false;
course->display(screen, top_line);
// Check for collisions.
for(int i=y; i<y+settings.car_size; i++)
for(int j=x; j<x+settings.car_size; j++)
if(course->taken(i, j) && car->collision_check(i-y, j-x))
survive=false;
if(survive)
car->display(screen, y-top_line, x);
else
car->explode(screen, y-top_line, x);
wrefresh(screen);
}
return survive;
}
car_image::car_image(void)
{
character = settings.character;
color = COLOR_YELLOW;
size = settings.car_size;
// The coolest part - drawing the damned thing
_clear();
// Original key points were (0,0), (1,4), (2,0), (3,4) and (4,0).
// Now we just need to scale them and draw lines between.
_line(0, 0, 1*(size-1)/4, 4*(size-1)/4); _line(1*(size-1)/4, 4*(size-1)/4, 2*(size-1)/4, 0);
_line(2*(size-1)/4, 0, 3*(size-1)/4, 4*(size-1)/4);
_line(3*(size-1)/4, 4*(size-1)/4, 4*(size-1)/4, 0);
}
void car_image::_clear(void)
{
// Simply clear the image
for(int i=0; i<size; i++)
for(int j=0; j<size; j++)
storage[i][j]=false; }
void car_image::display(WINDOW* screen, int y, int x)
{
// We don't need to store this anywhere, so it can be set ad-hoc.
wattron(screen, COLOR_PAIR(color));
wattron(screen, A_BOLD);
for(int i=0; i<size; i++)
{
for(int j=0; j<size; j++)
if(storage[i][j])
mvwaddch(screen, y+i, x+j, character);
}
// Restore the normal color for everything else...
wattroff(screen, COLOR_PAIR(color));
wattroff(screen, A_BOLD);
}
void car_image::explode(WINDOW* screen, int y, int x)
{// Even in ASCII we can do cool explosions :>
for(int i=0; i<size; i++)
{
for(int j=0; j<size; j++)
if(rand()%2)
{
int color = rand()%7 + 1;
wattron(screen, COLOR_PAIR(color));
mvwaddch(screen, y+i, x+j, '*');
wattroff(screen, COLOR_PAIR(color));
}
}
}
bool car_image::collision_check(int y, int x)
{
return storage[y][x];
}
vector<pair<int, int> > car_image::get_dots(void)
{
return dots;
}
/*
* This function is the heart of the original task for which this program
* was created. It is based on the simple formula, that for a line between
* (y1, x1) and (y2, x2) and a given coordinate x, the y coordinate for a
* point on a line is y1 + (y2-y1) / ( (x2-x1)/(x-x1) ).
*/
void car_image::_line(int y1, int x1, int y2, int x2)
{
// It can be given the other way round...
if(x2 < x1)
{
// So just swap the points.
int tmp = x1;
x1 = x2;
x2 = tmp;
// And swap the other coordinate.
tmp = y1;
y1 = y2;
y2 = tmp;
}
for(int i=x1; i<=x2; i++)
{
int y = y1 + (int)((float) (y2-y1)*(i-x1)/(x2-x1)+0.5);
// +0.5 is in order to do real rounding, not just truncation.
storage[y][i]=true;
dots.push_back(make_pair(y, i));
}
}