// 2>/dev/null; set -e; [ rocks.elf -nt "$0" ] || g++ -g -o rocks.elf -Wall -s "$0" -lX11; exec ./rocks.elf "$@" #include #include #include #include #include #include #include #include #include #include #include #include #define rdiv(r, base) floor(r / base) using namespace std; // stuff from libb ---------------------------------------- typedef double num; typedef unsigned long colour; #define pi M_PI #define deg2rad(a) (a * pi / 180.0) void vwarn(const char *format, va_list ap) { int l = strlen(format); char format1[l+1]; strcpy(format1, format); format1[l] = '\n'; format1[l+1] = '\0'; fflush(stdout); vfprintf(stderr, format1, ap); } void warn(const char *format, ...) { va_list ap; va_start(ap, format); vwarn(format, ap); va_end(ap); } void verror(const char *format, va_list ap) { vwarn(format, ap); exit(1); } void error(const char *format, ...) { va_list ap; va_start(ap, format); verror(format, ap); va_end(ap); } void rtime_to_timeval(num rtime, struct timeval *tv) { tv->tv_sec = (long)rtime; tv->tv_usec = (long)((rtime - tv->tv_sec) * 1e6); } num timeval_to_rtime(const struct timeval *tv) { return (num)tv->tv_sec + tv->tv_usec / 1e6; } void rtime_to_timespec(num rtime, struct timespec *ts) { ts->tv_sec = (long)rtime; ts->tv_nsec = (long)((rtime - ts->tv_sec) * 1e9); } num timespec_to_rtime(const struct timespec *ts) { return (num)ts->tv_sec + ts->tv_nsec / 1e9; } num rtime(void) { struct timeval tv; gettimeofday(&tv, NULL); return timeval_to_rtime(&tv); } num rsleep(num time) { if(time <= 0) { return 0; } struct timespec delay; rtime_to_timespec(time, &delay); if(nanosleep(&delay, &delay) == -1) { if(errno == EINTR) { return timespec_to_rtime(&delay); } return -1; } return 0; } typedef struct itimerval itimerval; void Ualarm(num dt) { itimerval v; rtime_to_timeval(dt, &v.it_value); v.it_interval.tv_sec = v.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &v, NULL); } bool lsleep_inited = 0; void catch_signal_null(int sig) { } sighandler_t sigact(int signum, sighandler_t handler, int sa_flags) { struct sigaction act, oldact; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = sa_flags; if(sigaction(signum, &act, &oldact) < 0) { return SIG_ERR; } return oldact.sa_handler; } void lsleep_init(void) { sigact(SIGALRM, catch_signal_null, SA_RESTART); lsleep_inited = 1; } long double asleep_small = 0.0001; void lsleep(num dt) { if(!lsleep_inited) { lsleep_init(); } // TODO it would be good to block then reenable signals somehow, // but don't think there is such a 'sleep'. Ualarm(dt); rsleep(dt+asleep_small/10); } long double asleep(long double dt, long double t) { if(dt <= 0.0) { return t; } t += dt; if(dt <= asleep_small * 2) { long double t1; while((t1=rtime()) < t) { // sched_yield might makes this busy loop a little less busy if other processes need to run? // any other way to do it? a real-time alarm? //sched_yield() } return t1; } else { lsleep(dt-asleep_small); long double t2 = rtime(); long double dt2 = t - t2; if(dt2 > 0) { return asleep(dt2, t2); } else if(dt < 0) { asleep_small *= 2; } return t2; } } long double csleep_last = 0; void csleep(long double step, bool sync, bool use_asleep, bool rush) { long double t = rtime(); long double to_sleep; if(!csleep_last) { // first step if(sync) { long double t1 = rdiv(t, step)*step+step; to_sleep = t1 - t; asleep(to_sleep, t); csleep_last = t1; } else { asleep(step, t); csleep_last = t + step; } } else { long double dt = t - csleep_last; long double to_sleep = step - dt; if(to_sleep > 0) { asleep(to_sleep, t); csleep_last += step; } else { // adjust for possible stoppage, etc if(sync) { csleep_last += (rdiv(dt, step)+1)*step; to_sleep = csleep_last - t; asleep(to_sleep, t); } else if(!rush) { csleep_last = t; } } } } int iclamp(int x, int min, int max) { return x < min ? min : x > max ? max : x; } colour rgb(num red_, num green_, num blue_); colour _hsv(num hue, num sat, num val); num rb_red_angle, rb_green_angle, rb_blue_angle, rb_red_power, rb_green_power, rb_blue_power; colour rb[360]; colour _rainbow(num a) { num r = rb_red_power * (cos(a-rb_red_angle)+1)/2; num g = rb_green_power * (cos(a-rb_green_angle)+1)/2; num b = rb_blue_power * (cos(a-rb_blue_angle)+1)/2; return rgb(r, g, b); } void rainbow_init(void) { rb_red_angle = deg2rad(-120); rb_green_angle = 0; rb_blue_angle = deg2rad(120); rb_red_power = 1; // rb_green_power = 0.8 rb_green_power = 0.9; rb_blue_power = 1; for(int i = 0; i<360; ++i) { rb[i] = _rainbow(deg2rad(i)); } } struct Rock; struct Ship; struct videoPrint_line; typedef struct Rock Rock; typedef struct Ship Ship; typedef struct videoPrint_line videoPrint_line; typedef complex cmplx; // handlers --------------------------------------------------------- typedef void (*Thunk)(void); typedef void (*OnOffHandler)(bool); // time scheduling code --------------------------------------------- typedef struct timeval timeval; bool operator<(const timeval &t1, const timeval &t2) { return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_usec < t2.tv_usec); } timeval current_time; typedef multimap schedule_t; schedule_t schedule; struct Rock { bool is_bouncy; double m; double r; cmplx x; cmplx v; cmplx a; int gender; bool to_be_removed; bool bent; }; struct Ship: public Rock { bool is_here; double t; double tv; bool thrusting, retroing, lefting, righting, hidden; int sg; cmplx forwards, right, p0, p1, p2; }; struct videoPrint_line { char *s; int col; int y; }; vector rocks(1); void delete_rock(Rock &r); void sched_check_events(void); void random_init(void); double randomd(void); void rocks_schedule_dump(void); void rocks_scheduler_gettime(void); void rocks_scheduler(void); void at(long at_sec, long at_usec, Thunk handler); void after(long sleep_sec, long sleep_usec, Thunk handler); void after_ms(long sleep_ms, Thunk handler); void bind(const char *keyname, Thunk handler); void bind2(const char *keyname, OnOffHandler handler); void unbind(const char *keyname); void command_keyboard(int keycode, int state, int press_release); void intro(void); void print_help(void); void warp(Rock &s); void bounce(Rock &r); void Rock_init(Rock &self); void move(Rock &r); void pop(Rock &r); bool gay(Rock &r); long rock_colour(Rock &r); void change_dress(Ship &self); void change_attraction(Ship &self); void change_gender(Ship &self); void change_sexuality(Ship &self); void calc(Ship &s); bool bent(Ship &s); void bang(void); void dump_rocks(void); bool check_side(Rock &r, Ship &s, cmplx p, cmplx q); void move(Ship &s); void thrust(bool p); void retro(bool p); void turnleft(bool p); void turnright(bool p); void hide(void); void sched_update(void); void pause_game(void); void delete_all_rocks(void); void random_level(void); void plant_rocks(void); void new_level(void); void next_level(void); void prev_level(void); void do_exit(void); void level_complete(void); double quad1(double a, double b, double c); double quad2(double a, double b, double c); void motion(Rock &r0, Rock &r1); void update(void); void sched_queerity(void); void queerity(void); void redraw(void); void configure_notify(void); void button_event(int button, int type, int x, int y); Bool true_pred(Display *f, XEvent *e, XPointer p); void check_events(void); void Ship_init(Ship &self); void start(void); int main(int main__argc, char *main__argv[]); void videoPrintf(const char *format, ...); void vvideoPrintf(const char *format, va_list ap); void videoPrint(const char *s); void videoPrint_render(void); void move(num x, num y); num text_width(char *p); num font_height(void); void gprint(char *p); void font(char *name, int size); void xfont(char *font_name); extern cmplx j; extern const char *font_name; extern int w; extern int h; extern int frame_sleep; extern double resistance; extern double G; extern double max_init_vel; extern double dt; extern int new_level_sleep; extern int sleep_of_death; extern int start_space; extern double light; extern double heavy; extern double ship_mass; extern double turn_rate; extern double thrust_rate; extern double retro_rate; extern bool rocks_warp; extern bool ship_warps; extern double max_orbit_vel; extern double prob_coed; extern double prob_bent; extern int min_queerity_sleep; extern int max_queerity_sleep; extern cmplx center; extern int level; extern bool paused; extern int after_cancel; extern bool scheduled; extern const char *compliments[]; extern int ncompliments; // global X11 data ----------------------------------------------------- Display *display; Window window; Pixmap doublebuffer; Colormap colormap; GC gc; XGCValues gcvalues; XFontStruct *_font; long black_; long white_; long red_; long blue_; long dred; long dblue; long green_; long grey_; long text_fade[256]; XEvent event; // key handlers ----------------------------------------------- map key_press_release_handlers; map key_press_handlers; Ship ship; XPoint points[4]; int videoPrint_y; num lx, ly; vector videoPrint_lines; cmplx j = cmplx(0,1); // configuration --------------------------------------------- const char *font_name = "-adobe-helvetica-medium-r-normal--11-80-100-100-p-56-iso8859-1"; int w = 800; int h = 480; int frame_sleep = 10; double resistance = 0.008; double G = 1.5; double max_init_vel = 1.0; double dt = 0.5; int new_level_sleep = 3000; int sleep_of_death = 2000; int start_space = 70; double light = 25.0; double heavy = 900.0; double ship_mass = 150.0; double turn_rate = 0.01; double thrust_rate = 0.03; double retro_rate = 0.01; bool rocks_warp = 0; bool ship_warps = 0; double max_orbit_vel = 3.0; double prob_coed = 1; double prob_bent = 0.2; int min_queerity_sleep = 20000; int max_queerity_sleep = 30000; // globals ------------------------------------------------ cmplx center = cmplx(w/2, h/2); // level data --------------------------------------------- int level = 1; double g; bool coed; int gob; int sg; double level_orbit; // state -------------------------------------------------- bool paused = 0; int after_cancel = 0; bool scheduled = 0; void random_init(void) { srandom(time(NULL)); } double randomd(void) { return random()/(double)RAND_MAX; } void rocks_schedule_dump(void) { schedule_t::iterator i = schedule.begin(); schedule_t::const_iterator end = schedule.end(); warn("%s\n", "- schedule -"); for(; i != end; ++i) { warn("time: %ld : %ld\n", i->first.tv_sec, i->first.tv_usec); } warn("%s\n", "------------"); } void rocks_scheduler_gettime(void) { gettimeofday(¤t_time, NULL); } void rocks_scheduler(void) { csleep(0, 0, 1, 0); while(1) { rocks_scheduler_gettime(); schedule_t::iterator i = schedule.begin(); for(; i != schedule.end(); ++i) { if(i->first < current_time) { (i->second)(); } else { num dt = timeval_to_rtime(&i->first) - timeval_to_rtime(¤t_time); csleep(dt, 0, 1, 0); break; } } schedule.erase(schedule.begin(), i); } } void at(long at_sec, long at_usec, Thunk handler) { timeval tv; tv.tv_sec = at_sec; tv.tv_usec = at_usec; schedule.insert(make_pair(tv,handler)); } void after(long sleep_sec, long sleep_usec, Thunk handler) { long at_sec = current_time.tv_sec + sleep_sec; long at_usec = current_time.tv_usec + sleep_usec; if(at_usec > 1000000) { at_usec -= 1000000; ++at_sec; } at(at_sec, at_usec, handler); } void after_ms(long sleep_ms, Thunk handler) { after(sleep_ms / 1000, (sleep_ms % 1000) * 1000, handler); } void bind(const char *keyname, Thunk handler) { KeySym keysym = XStringToKeysym(keyname); key_press_handlers[keysym] = handler; } void bind2(const char *keyname, OnOffHandler handler) { KeySym keysym = XStringToKeysym(keyname); key_press_release_handlers[keysym] = handler; } void unbind(const char *keyname) { KeySym keysym = XStringToKeysym(keyname); key_press_handlers.erase(keysym); key_press_release_handlers.erase(keysym); } KeySym my_XKeycodeToKeysym(Display *display, KeyCode keycode, int index) { KeySym ks = 0; int keysyms_per_keycode_return; KeySym *keysym = XGetKeyboardMapping(display, keycode, 1, &keysyms_per_keycode_return); if (index < keysyms_per_keycode_return) ks = keysym[index]; XFree(keysym); return ks; } void command_keyboard(int keycode, int state, int press_release) { int shift = state & ShiftMask ? 1 : 0; KeySym keysym = my_XKeycodeToKeysym(display, keycode, shift); if(press_release == 1) { Thunk handler = key_press_handlers[keysym]; if(handler != NULL) { (*handler)(); return; } } OnOffHandler handler = key_press_release_handlers[keysym]; if(handler != NULL) { (*handler)(press_release); } else {} } // text ------------------------------------------------------ void intro(void) { videoPrint("rocks 1.4"); videoPrint(""); videoPrint("Written by Sam Watkins in 2003"); videoPrint("This is public domain software."); videoPrint("Type `h' if you don't know how to play."); videoPrint(""); } void print_help(void) { videoPrint(""); videoPrint("Your Mission - Pop the Rocks!"); videoPrint(""); videoPrint(" z left"); videoPrint(" x right"); videoPrint(" enter thrust"); videoPrint(" / retro"); videoPrint(""); videoPrint(" space hide"); videoPrint(" q bang!"); videoPrint(""); videoPrint(" - previous level"); videoPrint(" = restart level"); videoPrint(" + next level"); videoPrint(""); videoPrint(" h help"); videoPrint(" p pause"); videoPrint(" Escape quit"); videoPrint(""); videoPrint("Each rock has a gender and preference!"); videoPrint(""); videoPrint(" Dark red: straight male"); videoPrint(" Dark blue: straight female"); videoPrint(" Bright red: gay male"); videoPrint(" Bright blue: gay female"); videoPrint(""); videoPrint("This determines how they interact,"); videoPrint("for example dark blue and dark red attract eachother,"); videoPrint("and a dark blue will chase a bright red, which will run away!"); videoPrint(""); videoPrint("Your ship also has gender and preference, but initially you don't know what."); videoPrint("You can guess what gender you are based on whether rocks are attracted to"); videoPrint("or repelled from you and whether you are attracted to or repelled from them."); videoPrint("Your gender and preference change from time to time. A little message"); videoPrint("alerts you to this."); videoPrint(""); videoPrint("Have fun!"); videoPrint(""); } const char *compliments[] = { "Right on, commander!", "Great flying!", "Awesome effort!", "Are you addicted yet?", "Let's see you pass the NEXT level!", "You had me worried for a minute there!", "Fantasic!", "Go for the record!", "This is just too easy for you, isn't it?", "No one got past that rock before!", NULL }; int ncompliments = 10; void warp(Rock &s) { if(s.x.real() < 0) { s.x = cmplx(s.x.real()+w, s.x.imag()); } if(s.x.real() > w) { s.x = cmplx(s.x.real()-w, s.x.imag()); } if(s.x.imag() < 0) { s.x = cmplx(s.x.real(), s.x.imag()+h); } if(s.x.imag() > h) { s.x = cmplx(s.x.real(), s.x.imag()-h); } } void bounce(Rock &r) { if(r.x.real() < r.r) { r.x = cmplx(2*r.r-r.x.real(), r.x.imag()); r.v = cmplx(-r.v.real(), r.v.imag()); } else if(r.x.real() > w - r.r) { r.x = cmplx(2*(w-r.r)-r.x.real(), r.x.imag()); r.v = cmplx(-r.v.real(), r.v.imag()); } if(r.x.imag() < r.r) { r.x = cmplx(r.x.real(), 2*r.r-r.x.imag()); r.v = cmplx(r.v.real(), -r.v.imag()); } else if(r.x.imag() > h - r.r) { r.x = cmplx(r.x.real(), 2*(h-r.r)-r.x.imag()); r.v = cmplx(r.v.real(), -r.v.imag()); } } void Rock_init(Rock &self) { self.is_bouncy = 1; double q = -0.5; self.m = pow(randomd()*(pow(heavy,q)-pow(light,q))+pow(light,q), 1/q); self.r = sqrt(self.m); while(1) { self.x = cmplx(randomd() * (w-self.r*2) + self.r, randomd() * (h-self.r*2) + self.r); if(ship.is_here && abs(self.x - ship.x) < start_space) { continue; } bool overlapping = 0; vector::iterator r = rocks.begin()+1; vector::const_iterator r1 = rocks.end(); for(; r != r1; ++r) { if(abs(self.x - (*r)->x) < self.r + (*r)->r) { overlapping = 1; break; } } if(! overlapping) { break; } } self.v = randomd()*max_init_vel * exp(j*randomd()*2.0*pi); cmplx d, u; d = self.x - center; u = d/abs(d); self.v += u*j * level_orbit / abs(d) * double(h) * max_orbit_vel; if(coed) { self.gender = int(randomd()*2)*2 - 1; } else { self.gender = gob; } self.a = 0.0; self.to_be_removed = 0; self.bent = randomd() 0; } long rock_colour(Rock &r) { long c; if(r.gender < 0) { if(gay(r)) { c = blue_; } else { c = dblue; } } else if(r.gender > 0) { if(gay(r)) { c = red_; } else { c = dred; } } else { c = grey_; } return c; } void change_dress(Ship &self) { if(paused) { return; } self.gender = -self.gender; self.sg = - self.sg; videoPrint("I'm a sweet transvestite..."); } void change_attraction(Ship &self) { if(paused) { return; } self.sg = - self.sg; videoPrint("A shiver runs down your spine..."); } void change_gender(Ship &self) { if(paused) { return; } self.gender = -self.gender; videoPrint("Ouch! that hurt"); } void change_sexuality(Ship &self) { int i = int(randomd()*3); if(i == 0) { change_dress(self); } else if(i == 1) { change_attraction(self); } else { change_gender(self); } } void calc(Ship &s) { s.forwards = cmplx(sin(s.t), cos(s.t)) * s.r; s.right = s.forwards / j; s.p0 = 2.0*s.forwards + s.x; s.p1 = s.right-s.forwards + s.x; s.p2 = -s.right-s.forwards + s.x; } bool bent(Ship &s) { return s.sg == sg; } void bang(void) { if(paused) { return; } videoPrint("bang!"); videoPrint(""); unbind("Return"); unbind("slash"); unbind("z"); unbind("x"); unbind("q"); unbind("space"); unbind("p"); unbind("plus"); unbind("equal"); unbind("minus"); rocks[0] = NULL; ship.is_here = 0; after_ms(sleep_of_death, start); } void dump_rocks(void) { warn("rocks:\n"); if(rocks[0] != NULL) { printf("ship "); } else { printf("dead "); } int i,l; l = rocks.size(); for(i=1; ito_be_removed) { printf("X "); } else { printf("O "); } } printf("\n"); } bool check_side(Rock &r, Ship &s, cmplx p, cmplx q) { cmplx pq, pqu, pr, d; double dpq, dx, dy; pq = q-p; dpq = abs(pq); pqu = pq/dpq; pr = r.x-p; // dpr = abs(pr); d = pr / pqu; dx = d.real(); dy = d.imag(); if((dy < r.r && dy >= 0.0 && dx > 0.0 && dx < dpq) || (abs(pr) < r.r)) { if(abs(pr) < r.r && p == s.p0) { pop(r); return 1; } else { bang(); return 1; } } return 0; } void move(Ship &s) { if(bent(s)) { s.a = -s.a; } if(s.thrusting) { s.a += s.forwards * thrust_rate; } if(s.retroing) { s.a -= s.forwards * retro_rate; } if(s.lefting) { s.tv -= turn_rate * dt; } if(s.righting) { s.tv += turn_rate * dt; } s.v += s.a * dt; s.v *= pow(1-resistance, dt); s.tv *= pow(1-resistance, dt); s.a = 0; s.x += s.v * dt; s.t += s.tv * dt; if(ship_warps) { warp(s); } else { bounce(s); } calc(s); if(! s.is_bouncy) { vector::iterator r = rocks.begin(); vector::const_iterator r1 = rocks.end(); for(; r != r1; ++r) { check_side(**r, s, s.p0,s.p1) || check_side(**r, s, s.p1,s.p2) || check_side(**r, s, s.p2,s.p0); if(rocks[0] == NULL) { return; } } } } void thrust(bool p) { ship.thrusting = p; } void retro(bool p) { ship.retroing = p; } void turnleft(bool p) { ship.lefting = p; } void turnright(bool p) { ship.righting = p; } void hide(void) { if(paused) { return; } ship.hidden = ! ship.hidden; ship.is_bouncy = ship.hidden; if(ship.hidden) { videoPrint("where'd he go?"); } else { videoPrint("thar she blows!"); } } void sched_update(void) { if(! scheduled) { scheduled = 1; after_ms(frame_sleep, update); } } void pause_game(void) { paused = ! paused; if(! paused) { sched_update(); } } void delete_all_rocks(void) { vector::iterator r = rocks.begin()+1; vector::const_iterator r1 = rocks.end(); for(; r != r1; ++r) { delete(*r); } rocks.resize(1); } void random_level(void) { coed = randomd() < prob_coed; if(! coed) { gob = int(randomd()*2)*2-1; } sg = int(randomd()*2)*2-1; level_orbit = randomd()-0.5; } void plant_rocks(void) { after_cancel -= 1; if(after_cancel == 0) { if(rocks[0] == NULL) { return; } int i; for(i=0; i 0) { level -= 1; } new_level(); } void do_exit(void) { XUndefineCursor(display, window); XUngrabKeyboard(display, CurrentTime); exit(0); } void level_complete(void) { if(ncompliments>0) { int i = int(randomd()*ncompliments); videoPrint(compliments[i]); for(; i=1; --a) { r = rocks[a]; if(r->to_be_removed) { delete r; for(b=a+1; b<=high; ++b) { rocks[b-1] = rocks[b]; } --high; rocks.resize(high+1); if(high == 0) { level_complete(); goto complete; } } else { move(*r); } } for(a=low+1; a<=high; ++a) { for(b=low; b::iterator r = rocks.begin()+1; vector::const_iterator r1 = rocks.end(); for(; r != r1; ++r) { cmplx x = (*r)->x; double rad = (*r)->r; gcvalues.foreground = rock_colour(**r); XChangeGC(display, gc, GCForeground, &gcvalues); XFillArc(display, doublebuffer, gc, int(x.real() - rad), h-int(x.imag() + rad), int(rad*2), int(rad*2), 0, 64*360); gcvalues.foreground = white_; XChangeGC(display, gc, GCForeground, &gcvalues); XDrawArc(display, doublebuffer, gc, int(x.real() - rad), h-int(x.imag() + rad), int(rad*2), int(rad*2), 0, 64*360); } if(ship.is_here && ! ship.hidden) { points[0].x = int(ship.p0.real()); points[0].y = h-int(ship.p0.imag()); points[1].x = int(ship.p1.real()); points[1].y = h-int(ship.p1.imag()); points[2].x = int(ship.p2.real()); points[2].y = h-int(ship.p2.imag()); points[3].x = int(ship.p0.real()); points[3].y = h-int(ship.p0.imag()); gcvalues.foreground = green_; XChangeGC(display, gc, GCForeground, &gcvalues); XFillPolygon(display, doublebuffer, gc, points, 4, Convex, CoordModeOrigin); gcvalues.foreground = white_; XChangeGC(display, gc, GCForeground, &gcvalues); XDrawLines(display, doublebuffer, gc, points, 4, CoordModeOrigin); } videoPrint_render(); gcvalues.tile = doublebuffer; gcvalues.fill_style = FillTiled; XChangeGC(display, gc, GCTile|GCFillStyle, &gcvalues); XFillRectangle(display, window, gc, 0, 0, w, h); gcvalues.fill_style = FillSolid; XChangeGC(display, gc, GCTile|GCFillStyle, &gcvalues); } void configure_notify(void) { } void button_event(int button, int type, int x, int y) { if(button >= 1 && button <= 3) {} else {} } // the X11 event loop -------------------------------------------------- Bool true_pred(Display *f, XEvent *e, XPointer p) { return True; } void check_events(void) { unsigned int button = 0; while(XCheckIfEvent(display, &event, true_pred, NULL)) { switch(event.type) { case Expose: ; if((event.xexpose.count == 0)) { redraw(); } break; case ConfigureNotify: configure_notify(); break; case ButtonPress: if(button != 0) {} else { button = event.xbutton.button; button_event(button, 0, event.xbutton.x, event.xbutton.y); } break; case MotionNotify: ; while(XCheckTypedEvent(display, MotionNotify, &event)) { } button_event(button, 1, event.xmotion.x, event.xmotion.y); break; case ButtonRelease: if(button == 0) {} else if(event.xbutton.button != button) {} else { button_event(button, 2, event.xbutton.x, event.xbutton.y); button = 0; } break; case KeyPress: command_keyboard(event.xkey.keycode, event.xkey.state, 1); break; case KeyRelease: command_keyboard(event.xkey.keycode, event.xkey.state, 0); break; case MapNotify: ; break; case UnmapNotify: ; break; case ReparentNotify: break; break; default: warn("unhandled event, type: 0x%04x\n", event.type); } } sched_check_events(); } // Ship init ----------------------------------------------------------- void Ship_init(Ship &self) { self.is_here = 1; self.is_bouncy = 0; self.x = center; self.v = 0; self.a = 0; self.t = 0; self.tv = 0; self.r = 10; self.thrusting = 0; self.retroing = 0; self.lefting = 0; self.righting = 0; self.hidden = 0; self.m = ship_mass; self.gender = int(randomd()*2)*2 - 1; self.sg = int(randomd()*2)*2 - 1; bind2("Return", thrust); bind2("slash", retro); bind2("z", turnleft); bind2("x", turnright); bind("q", bang); bind("space", hide); bind("p", pause_game); bind("plus", next_level); bind("equal", new_level); bind("minus", prev_level); } // the main program ---------------------------------------------------- void start(void) { Ship_init(ship); rocks[0] = &ship; new_level(); sched_update(); } void sched_check_events(void) { after_ms(frame_sleep, check_events); } int main(int main__argc, char *main__argv[]) { if((display = XOpenDisplay(NULL)) == NULL) { error("cannot open display\n"); } bind("h", print_help); bind("Escape", do_exit); Window root; XColor color; int screen_number; screen_number = DefaultScreen(display); white_ = WhitePixel(display, screen_number); black_ = BlackPixel(display, screen_number); colormap = DefaultColormap(display, screen_number); red_ = blue_ = dred = dblue = green_ = grey_ = white_; if(XAllocNamedColor(display, colormap, "red", &color, &color)) { red_ = color.pixel; } if(XAllocNamedColor(display, colormap, "blue", &color, &color)) { blue_ = color.pixel; } if(XAllocNamedColor(display, colormap, "#550000", &color, &color)) { dred = color.pixel; } if(XAllocNamedColor(display, colormap, "#000055", &color, &color)) { dblue = color.pixel; } if(XAllocNamedColor(display, colormap, "#005500", &color, &color)) { green_ = color.pixel; } if(XAllocNamedColor(display, colormap, "#333333", &color, &color)) { grey_ = color.pixel; } rainbow_init(); for(int i=0; i<256; ++i) { num g = i/256.0; text_fade[255-i] = rgb(g, g, g); } if((_font = XLoadQueryFont(display, font_name)) == NULL) { error("cannot load font\n"); } gc = DefaultGC(display, screen_number); gcvalues.function = GXcopy; gcvalues.foreground = white_; gcvalues.cap_style = CapNotLast; gcvalues.line_width = 0; gcvalues.font = _font->fid; XChangeGC(display, gc, GCFunction|GCForeground|GCCapStyle|GCLineWidth|GCFont, &gcvalues); root = DefaultRootWindow(display); unsigned long valuemask = 0; XSetWindowAttributes attributes; valuemask |= CWOverrideRedirect; attributes.override_redirect = True; // window = XCreateSimpleWindow(display, root, 0, 0, w, h, 0, white_, black_); window = XCreateWindow(display, root, 0, 0, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, valuemask, &attributes); doublebuffer = XCreatePixmap(display, window, w, h, XDefaultDepth(display, screen_number)); XSelectInput(display, window, ExposureMask|ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask); XMapWindow(display, window); XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime); // Hide the cursor Cursor invisibleCursor; Pixmap bitmapNoData; XColor black; static char noData[] = { 0,0,0,0,0,0,0,0 }; black.red = black.green = black.blue = 0; bitmapNoData = XCreateBitmapFromData(display, window, noData, 8, 8); invisibleCursor = XCreatePixmapCursor(display, bitmapNoData, bitmapNoData, &black, &black, 0, 0); XDefineCursor(display,window, invisibleCursor); XFreeCursor(display, invisibleCursor); XFreePixmap(display, bitmapNoData); rocks_scheduler_gettime(); intro(); random_init(); start(); sched_queerity(); sched_check_events(); rocks_scheduler(); return 0; } // video print --------------------------------------------------------- void videoPrintf(const char *format, ...) { va_list ap; va_start(ap, format); vvideoPrintf(format, ap); va_end(ap); } void vvideoPrintf(const char *format, va_list ap) { char b[1024]; vsnprintf(b, sizeof(b), format, ap); b[sizeof(b)-1] = '\0'; videoPrint(b); } void videoPrint(const char *s) { // printf("%s\n", s); if(!videoPrint_lines.size()) { videoPrint_y = 5; } videoPrint_line *l = new videoPrint_line; l->s = strdup(s); l->col = 0; l->y = videoPrint_y += font_height(); videoPrint_lines.push_back(l); } void videoPrint_render(void) { move(w/2, 5); int o = 0; vector::iterator i; for(i = videoPrint_lines.begin() ; i != videoPrint_lines.end() ; ++i) { videoPrint_line *l = *i; char *s = l->s; int col = l->col++; gcvalues.foreground = text_fade[col/2]; XChangeGC(display, gc, GCForeground, &gcvalues); int tw = text_width(s); move(w/2 - tw/2, ly); gprint(s); if(col == 511) { free(s); delete l; } else { videoPrint_lines[o] = l; ++o; } } videoPrint_lines.resize(o); } // more stuff from libb ----------------------------------- void move(num x, num y) { lx = x; ly = y; } num text_width(char *p) { int len = strlen(p); return XTextWidth(_font, p, len); } num font_height(void) { return _font->ascent + _font->descent; } void gprint(char *p) { int len = strlen(p); int text_width = XTextWidth(_font, p, len); (void)text_width; XDrawString(display, doublebuffer, gc, lx, ly+_font->ascent, p, len); move(lx, ly + font_height()); } void font(char *name, int size) { char xfontname[1024]; snprintf(xfontname, sizeof(xfontname), "-*-%s-r-normal--%d-*-100-100-p-*-iso8859-1", name, size); xfontname[sizeof(xfontname)-1] = '\0'; xfont(xfontname); } void xfont(char *font_name) { if((_font = XLoadQueryFont(display, font_name)) == NULL) { error("cannot load font %s", font_name); } gcvalues.font = _font->fid; XChangeGC(display, gc, GCFont, &gcvalues); } colour rgb(num red_, num green_, num blue_) { char name[8]; int r, g, b; r = iclamp(red_*256, 0, 255); g = iclamp(green_*256, 0, 255); b = iclamp(blue_*256, 0, 255); snprintf(name, sizeof(name), "#%02x%02x%02x", r, g, b); XColor colour; if(XAllocNamedColor(display, colormap, name, &colour, &colour)) { return colour.pixel; } return white_; } colour _hsv(num hue, num sat, num val) { num r = rb_red_power * (cos(hue-rb_red_angle)+1)/2; num g = rb_green_power * (cos(hue-rb_green_angle)+1)/2; num b = rb_blue_power * (cos(hue-rb_blue_angle)+1)/2; r *= sat; g *= sat; b *= sat; r = 1-(1-r)*(1-val); g = 1-(1-g)*(1-val); b = 1-(1-b)*(1-val); return rgb(r, g, b); }