diff --git a/games/Makefile b/games/Makefile index b3922605..2aa77c37 100644 --- a/games/Makefile +++ b/games/Makefile @@ -9,6 +9,7 @@ LOCALBINARIES:=\ pong \ conway \ snake \ +asteroids \ BINARIES:=$(addprefix $(INITRDDIR)/,$(LOCALBINARIES)) diff --git a/games/asteroids.cpp b/games/asteroids.cpp new file mode 100644 index 00000000..80d9ff64 --- /dev/null +++ b/games/asteroids.cpp @@ -0,0 +1,841 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This define runs the game without actually setting the video mode and +// checking whether the frame was actually copied to the screen. useful for +// debugging the game since you can't see console output. +//#define HACK_DONT_CHECK_FB + +// TODO: Hacks that should belong in libm or something. +extern "C" float sqrtf(float x) +{ + float ret; + asm ("fsqrt" : "=t" (ret) : "0" (x)); + return ret; +} + +extern "C" void sincosf(float x, float* sinval, float* cosval) +{ + asm ("fsincos" : "=t" (*cosval), "=u" (*sinval) : "0" (x)); +} + +const float PI = 3.1415926532f; + +inline float RandomFloat() +{ + return (float) rand() / 32768.0f; +} + +inline float RandomFloat(float min, float max) +{ + return min + RandomFloat() * (max - min); +} + +inline float DegreeToRadian(float degree) +{ + return degree / 180 * PI; +} + +inline float RandomAngle() +{ + return RandomFloat() * DegreeToRadian(360); +} + +inline uint32_t MakeColor(uint8_t r, uint8_t g, uint8_t b) +{ + return b << 0UL | g << 8UL | r << 16UL; +} + +const size_t STARFIELD_WIDTH = 512UL; +const size_t STARFIELD_HEIGHT = 512UL; +uint32_t starfield[STARFIELD_WIDTH * STARFIELD_HEIGHT]; + +void GenerateStarfield(uint32_t* bitmap, size_t width, size_t height) +{ + size_t numpixels = width * height; + for ( size_t i = 0; i < numpixels; i++ ) + { + uint8_t color = 0; + int randval = rand() % 256; + bool isstar = randval == 5 || randval == 42 || randval == 101; + if ( isstar ) { color = rand(); } + bitmap[i] = MakeColor(color, color, color); + } +} + +const size_t MAXKEYNUM = 512UL; +bool keysdown[MAXKEYNUM] = { false }; + +void FetchKeyboardInput() +{ + // Read the keyboard input from the user. + const unsigned termmode = TERMMODE_KBKEY + | TERMMODE_UNICODE + | TERMMODE_SIGNAL + | TERMMODE_NONBLOCK; + if ( settermmode(0, termmode) ) { error(1, errno, "settermmode"); } + uint32_t codepoint; + ssize_t numbytes; + while ( 0 < (numbytes = read(0, &codepoint, sizeof(codepoint))) ) + { + int kbkey = KBKEY_DECODE(codepoint); + int abskbkey = (kbkey < 0) ? -kbkey : kbkey; + if ( MAXKEYNUM <= abskbkey ) { continue; } + keysdown[abskbkey] = 0 < kbkey; + } +} + +size_t xres; +size_t yres; +int fb; +size_t bpp; +size_t linesize; +size_t framesize; +uint32_t* buf; +bool gamerunning; +unsigned long framenum; + +void DrawLine(uint32_t color, long x0, long y0, long x1, long y1) +{ + long dx = labs(x1-x0); + long sx = x0 < x1 ? 1 : -1; + long dy = labs(y1-y0); + long sy = y0 < y1 ? 1 : -1; + long err = (dx>dy ? dx : -dy)/2L; + long e2; + while ( true ) + { + if ( 0 <= x0 && x0 < xres && 0 <= y0 & y0 < yres ) + { + size_t index = y0 * linesize + x0; + buf[index] = color; + } + if ( x0 == x1 && y0 == y1 ) { break; } + e2 = err; + if ( e2 > -dx ) { err -= dy; x0 += sx; } + if ( e2 < dy ) { err += dx; y0 += sy; } + } +} + +class Vector +{ +public: + float x; + float y; + +public: + Vector(float x = 0.0f, float y = 0.0f) : x(x), y(y) { } + + Vector& operator=(const Vector& rhs) + { + if ( this != &rhs ) { x = rhs.x; y = rhs.y; } + return *this; + } + + Vector& operator+=(const Vector& rhs) + { + x += rhs.x; + y += rhs.y; + } + + Vector& operator-=(const Vector& rhs) + { + x -= rhs.x; + y -= rhs.y; + } + + Vector& operator*=(float scalar) + { + x *= scalar; + y *= scalar; + } + + const Vector operator+(const Vector& other) const + { + Vector ret(*this); ret += other; return ret; + } + + const Vector operator-(const Vector& other) const + { + Vector ret(*this); ret -= other; return ret; + } + + const Vector operator*(float scalar) const + { + Vector ret(*this); ret *= scalar; return ret; + } + + bool operator==(const Vector& other) const + { + return x == other.x && y == other.y; + } + + bool operator!=(const Vector& other) const + { + return !(*this == other); + } + + float Dot(const Vector& other) const + { + return x * other.x + y * other.y; + } + + float SquaredSize() const + { + return x*x + y*y; + } + + float Size() const + { + return sqrtf(SquaredSize()); + } + + float DistanceTo(const Vector& other) const + { + return (other - *this).Size(); + } + + const Vector Rotate(float radians) const + { + float sinr; + float cosr; + sincosf(radians, &sinr, &cosr); + float newx = x * cosr - y * sinr; + float newy = x * sinr + y * cosr; + return Vector(newx, newy); + } + + const Vector RotateAround(float radians, const Vector& off) const + { + return Vector(*this - off).Rotate(radians) + off; + } +}; + +bool AboveLine(const Vector& a, const Vector& b, const Vector& p) +{ + Vector ba = b - a; + Vector bahat = Vector(ba.y, -ba.x); + Vector bp = p - a; + return 0.0 <= bahat.Dot(bp); +} + +bool InsideTriangle(const Vector& a, const Vector& b, const Vector& c, + const Vector& p) +{ + return !AboveLine(a, b, p) && !AboveLine(b, c, p) && !AboveLine(c, a, p); +} + +class Object; +class Actor; +class Spaceship; + +Object* firstobject = NULL; +Object* lastobject = NULL; +Vector screenoff; +Spaceship* playership = NULL; + +class Object +{ +public: + Object() + { + gcborn = false; + gcdead = false; + if ( !firstobject ) + { + firstobject = lastobject = this; + prevobj = nextobj = NULL; + } + else + { + lastobject->nextobj = this; + this->prevobj = lastobject; + this->nextobj = NULL; + lastobject = this; + } + }; + + virtual ~Object() + { + if ( !prevobj ) { firstobject = nextobj; } + else { prevobj->nextobj = nextobj; } + if ( !nextobj ) { lastobject = prevobj; } + else { nextobj->prevobj = prevobj; } + } + +public: + virtual bool IsA(const char* classname) + { + return !strcmp(classname, "Object"); + } + virtual void OnFrame(float deltatime) { } + virtual void Render() { } + +private: + bool gcborn; + bool gcdead; + Object* prevobj; + Object* nextobj; + +public: + bool GCIsBorn() const { return gcborn; } + bool GCIsDead() const { return gcdead; } + bool GCIsAlive() const { return GCIsBorn() && !GCIsDead(); } + void GCDie() { gcdead = true; } + void GCBirth() { gcborn = true; } + Object* NextObj() const { return nextobj; } + +}; + +class Actor : public Object +{ +public: + Actor() { } + virtual ~Actor() { } + +public: + virtual void OnFrame(float deltatime) + { + Think(deltatime); + Move(deltatime); + } + + virtual bool IsA(const char* classname) + { + return !strcmp(classname, "Actor") || Object::IsA(classname); + } + virtual void Move(float deltatime); + virtual void Think(float deltatime) { } + virtual void Render() { } + +public: + Vector pos; + Vector vel; + Vector acc; + +}; + +void Actor::Move(float deltatime) +{ + vel += acc * deltatime; + pos += vel * deltatime; +} + +class Asteroid : public Actor +{ +public: + Asteroid(Vector pos, Vector vel, float size); + virtual ~Asteroid() { } + virtual bool IsA(const char* classname) + { + return !strcmp(classname, "Asteroid") || Actor::IsA(classname); + } + virtual void Move(float deltatime); + virtual void Render(); + +private: + Vector Point(size_t id); + +public: + bool InsideMe(const Vector& p); + void OnHit(); + +private: + static const size_t MIN_POLYS = 5; + static const size_t MAX_POLYS = 12; + static const float MAX_TURN_SPEED = 50.0f; + size_t numpolygons; + float slice; + float polydists[MAX_POLYS+1]; + float size; + float angle; + float turnspeed; + +}; + +Asteroid::Asteroid(Vector pos, Vector vel, float size) +{ + this->pos = pos; + this->vel = vel; + this->size = size; + angle = 0.0f; + turnspeed = DegreeToRadian(MAX_TURN_SPEED) * (RandomFloat() * 2.0f - 1.0f); + numpolygons = MIN_POLYS + rand() % (MAX_POLYS - MIN_POLYS); + slice = DegreeToRadian(360.0f) / (float) numpolygons; + for ( size_t i = 0; i < numpolygons; i++ ) + { + polydists[i] = (RandomFloat() + 1.0) * size / 2.0; + } + polydists[numpolygons] = polydists[0]; +} + +void Asteroid::Move(float deltatime) +{ + Actor::Move(deltatime); + angle += turnspeed * deltatime; +} + +Vector Asteroid::Point(size_t i) +{ + float rot = i * slice + angle; + return Vector(polydists[i], 0.0).Rotate(rot); +} + +bool Asteroid::InsideMe(const Vector& p) +{ + const Vector& center = pos; + for ( size_t i = 0; i < numpolygons; i++ ) + { + Vector from = Point(i) + pos; + Vector to = Point(i+1) + pos; + if ( InsideTriangle(from, to, center, p) ) { return true; } + } + return false; +} + +void Asteroid::Render() +{ + Vector screenpos = pos - screenoff; + uint32_t color = MakeColor(200, 200, 200); + float slice = DegreeToRadian(360.0f) / (float) numpolygons; + for ( size_t i = 0; i < numpolygons; i++ ) + { + Vector from = Point(i) + screenpos; + Vector to = Point(i+1) + screenpos; + DrawLine(color, from.x, from.y, to.x, to.y); + } +} + +void Asteroid::OnHit() +{ + if ( !GCIsAlive() ) { return; } + Vector axis = Vector(size/2.0f, 0.0f).Rotate(RandomAngle()); + float sizea = RandomFloat(size*0.3, size*0.7); + float sizeb = RandomFloat(size*0.3, size*0.7); + const float MINIMUM_SIZE = 6.0; + const float MAX_ANGLE = DegreeToRadian(45); + if ( MINIMUM_SIZE <= sizea ) + { + Vector astvel = vel.Rotate(RandomFloat(0.0, MAX_ANGLE)) * 1.2; + new Asteroid(pos + axis, astvel, sizea); + } + if ( MINIMUM_SIZE <= sizeb ) + { + Vector astvel = vel.Rotate(RandomFloat(0.0, -MAX_ANGLE)) * 1.2; + new Asteroid(pos - axis, astvel, sizeb); + } + GCDie(); +} + +class AsteroidField : public Actor +{ +public: + AsteroidField() { } + virtual ~AsteroidField() { } + virtual bool IsA(const char* classname) + { + return !strcmp(classname, "AsteroidField") || Actor::IsA(classname); + } + +public: + virtual void Think(float deltatime); + +}; + +void AsteroidField::Think(float deltatime) +{ + float spawndist = 1500.0f; + float maxdist = 1.5 * spawndist; + size_t minimumasteroids = 200; + size_t numasteroids = 0; + Vector center = ((Actor*)playership)->pos; + for ( Object* obj = firstobject; obj; obj = obj->NextObj() ) + { + if ( !obj->IsA("Asteroid") ) { continue; } + Asteroid* ast = (Asteroid*) obj; + numasteroids++; + float dist = ast->pos.DistanceTo(center); + if ( spawndist < dist ) { ast->GCDie(); } + } + for ( ; numasteroids < minimumasteroids; numasteroids++ ) + { + float dist = RandomFloat(spawndist, maxdist); + Vector astpos = Vector(dist, 0.0f).Rotate(RandomAngle()) + center; + float minsize = 4.0; + float maxsize = 120.0f; + float maxspeed = 80.0f; + float size = RandomFloat(minsize, maxsize); + float speed = RandomFloat() * maxspeed; + Vector astvel = Vector(speed, 0.0).Rotate(RandomAngle()); + new Asteroid(astpos, astvel, size); + } +} + +class Missile : public Actor +{ +public: + Missile(Vector pos, Vector vel, float ttl); + virtual bool IsA(const char* classname) + { + return !strcmp(classname, "Missile") || Actor::IsA(classname); + } + virtual void Think(float deltatime); + virtual void Render(); + virtual ~Missile(); + +private: + float ttl; + +}; + +Missile::Missile(Vector pos, Vector vel, float ttl) +{ + this->pos = pos; + this->vel = vel; + this->ttl = ttl; +} + +void Missile::Think(float deltatime) +{ + ttl -= deltatime; + if ( ttl < 0 ) { GCDie(); } + for ( Object* obj = firstobject; obj; obj = obj->NextObj() ) + { + if ( !obj->GCIsAlive() ) { continue; } + if ( !obj->IsA("Asteroid") ) { continue; } + Asteroid* ast = (Asteroid*) obj; + if ( !ast->InsideMe(pos) ) { continue; } + ast->OnHit(); + GCDie(); + } +} + +void Missile::Render() +{ + Vector screenpos = pos - screenoff; + uint32_t shipcolor = MakeColor(31, 255, 31); + const float MISSILE_LEN = 5.0f; + Vector from = screenpos; + Vector to = screenpos + vel * (MISSILE_LEN / vel.Size()); + DrawLine(shipcolor, from.x, from.y, to.x, to.y); +} + +Missile::~Missile() +{ +} + +class Spaceship : public Actor +{ +public: + Spaceship(float shipangle, + Vector pos = Vector(0, 0), + Vector vel = Vector(0, 0), + Vector acc = Vector(0, 0)); + virtual ~Spaceship(); + +public: + virtual bool IsA(const char* classname) + { + return !strcmp(classname, "Spaceship") || Actor::IsA(classname); + } + virtual void Think(float deltatime); + virtual void Render(); + +public: + void SetThrust(bool forward, bool backward); + void SetTurn(bool turnleft, bool turnright); + void SetFiring(bool firing); + +private: + bool turnleft; + bool turnright; + bool moveforward; + bool movebackward; + bool firing; + float shipangle; + +}; + +Spaceship::Spaceship(float shipangle, Vector pos, Vector vel, Vector acc) +{ + this->shipangle = shipangle; + this->pos = pos; + this->vel = vel; + this->acc = acc; + turnleft = turnright = moveforward = movebackward = firing = false; +} + +Spaceship::~Spaceship() +{ +} + +void Spaceship::Think(float deltatime) +{ + for ( Object* obj = firstobject; obj; obj = obj->NextObj() ) + { + if ( !obj->GCIsAlive() ) { continue; } + if ( !obj->IsA("Asteroid") ) { continue; } + Asteroid* ast = (Asteroid*) obj; + if ( !ast->InsideMe(pos) ) { continue; } + ast->OnHit(); + pos.y = 16384 - pos.y; + break; + } + const float turnspeed = 100.0; + const float turnamount = turnspeed * deltatime; + if ( turnleft ) { shipangle -= DegreeToRadian(turnamount); } + if ( turnright ) { shipangle += DegreeToRadian(turnamount); } + float shipaccelamount = 15.0; + float shipaccel = 0.0; + if ( moveforward ) { shipaccel += shipaccelamount; } + if ( movebackward ) { shipaccel -= shipaccelamount; } + acc = Vector(shipaccel, 0.0).Rotate(shipangle); + float shipspeed = vel.Size(); + float maxspeed = 50.0f; + if ( maxspeed < shipspeed ) { vel *= maxspeed / shipspeed; } + if ( firing ) + { + float ttl = 8.0; + float speed = 120.0; + const Vector P3(16.0f, 0.0f); + Vector spawnpos = pos + P3.Rotate(shipangle) * 1.1; + Vector spawnvel = Vector(speed, 0.0).Rotate(shipangle); + new Missile(spawnpos, spawnvel, ttl); + } +} + +void Spaceship::Render() +{ + Vector screenpos = pos - screenoff; + // TODO: Ideally these should be global constants, but global constructors + // are _not_ called upon process initiazation on Sortix yet. + const Vector P1(-8.0f, 8.0f); + const Vector P2(-8.0f, -8.0f); + const Vector P3(16.0f, 0.0f); + Vector p1 = P1.Rotate(shipangle) + screenpos; + Vector p2 = P2.Rotate(shipangle) + screenpos; + Vector p3 = P3.Rotate(shipangle) + screenpos; + uint32_t shipcolor = MakeColor(255, 255, 255); + DrawLine(shipcolor, p1.x, p1.y, p2.x, p2.y); + DrawLine(shipcolor, p2.x, p2.y, p3.x, p3.y); + DrawLine(shipcolor, p1.x, p1.y, p3.x, p3.y); +} + +void Spaceship::SetThrust(bool forward, bool backward) +{ + this->moveforward = forward; + this->movebackward = backward; +} + +void Spaceship::SetTurn(bool turnleft, bool turnright) +{ + this->turnleft = turnleft; + this->turnright = turnright; +} + +void Spaceship::SetFiring(bool firing) +{ + this->firing = firing; +} + +uintmax_t lastframeat; + +void GameLogic() +{ + uintmax_t now; + do uptime(&now); + while ( now == lastframeat); + unsigned long deltausecs = now - lastframeat; + lastframeat = now; + float deltatime = deltausecs / 1000000.0f; + float timescale = 3.0; + deltatime *= timescale; + Object* first = firstobject; + Object* obj; + for ( obj = first; obj; obj = obj->NextObj() ) { obj->GCBirth(); } + playership->SetThrust(keysdown[KBKEY_UP], keysdown[KBKEY_DOWN]); + playership->SetTurn(keysdown[KBKEY_LEFT], keysdown[KBKEY_RIGHT]); + playership->SetFiring(keysdown[KBKEY_SPACE]); + keysdown[KBKEY_SPACE] = false; + for ( obj = first; obj; obj = obj->NextObj() ) + { + if ( !obj->GCIsBorn() ) { continue; } + obj->OnFrame(deltatime); + } + for ( obj = first; obj; ) + { + Object* todelete = obj; + obj = obj->NextObj(); + if ( !todelete->GCIsDead() ) { continue; } + delete todelete; + } +} + +void Render() +{ + screenoff = playership->pos - Vector(xres/2.0, yres/2.0); + size_t staroffx = (size_t) screenoff.x; + size_t staroffy = (size_t) screenoff.y; + for ( size_t y = 0; y < yres; y++ ) + { + uint32_t* line = buf + y * linesize; + for ( size_t x = 0; x < xres; x++ ) + { + size_t fieldx = (x+staroffx) % STARFIELD_WIDTH; + size_t fieldy = (y+staroffy) % STARFIELD_HEIGHT; + size_t fieldindex = fieldy * STARFIELD_HEIGHT + fieldx; + line[x] = starfield[fieldindex]; + } + } + + for ( Object* obj = firstobject; obj; obj = obj->NextObj() ) + { + obj->Render(); + } +} + +void FlushBuffer() +{ + lseek(fb, 0, SEEK_SET); + if ( writeall(fb, buf, framesize) < framesize ) + { +#ifndef HACK_DONT_CHECK_FB + error(1, errno, "writing to framebuffer"); +#endif + } +} + +void RunFrame() +{ + FetchKeyboardInput(); + GameLogic(); + Render(); + FlushBuffer(); +} + +char* GetCurrentVideoMode() +{ + FILE* fp = fopen("/dev/video/mode", "r"); + if ( !fp ) { return NULL; } + char* mode = NULL; + size_t n = 0; + getline(&mode, &n, fp); + fclose(fp); + return mode; +} + +// TODO: This should be in libc and not use libmaxsi. +#include +#include +#include +using namespace Maxsi; +bool ReadParamString(const char* str, ...) +{ + if ( String::Seek(str, '\n') ) { Error::Set(EINVAL); } + const char* keyname; + va_list args; + while ( *str ) + { + size_t varlen = String::Reject(str, ","); + if ( !varlen ) { str++; continue; } + size_t namelen = String::Reject(str, "="); + if ( !namelen ) { Error::Set(EINVAL); goto cleanup; } + if ( !str[namelen] ) { Error::Set(EINVAL); goto cleanup; } + if ( varlen < namelen ) { Error::Set(EINVAL); goto cleanup; } + size_t valuelen = varlen - 1 /*=*/ - namelen; + char* name = String::Substring(str, 0, namelen); + if ( !name ) { goto cleanup; } + char* value = String::Substring(str, namelen+1, valuelen); + if ( !value ) { delete[] name; goto cleanup; } + va_start(args, str); + while ( (keyname = va_arg(args, const char*)) ) + { + char** nameptr = va_arg(args, char**); + if ( String::Compare(keyname, name) ) { continue; } + *nameptr = value; + break; + } + va_end(args); + if ( !keyname ) { delete[] value; } + delete[] name; + str += varlen; + str += String::Accept(str, ","); + } + return true; + +cleanup: + va_start(args, str); + while ( (keyname = va_arg(args, const char*)) ) + { + char** nameptr = va_arg(args, char**); + delete[] *nameptr; *nameptr = NULL; + } + va_end(args); + return false; +} + +int atoi_safe(const char* str) +{ + if ( !str ) { return 0; } + return atoi(str); +} + +void InitGame() +{ + uptime(&lastframeat); + GenerateStarfield(starfield, STARFIELD_WIDTH, STARFIELD_HEIGHT); + playership = new Spaceship(0.0, Vector(0, 0), Vector(4.0f, 0)); + new AsteroidField; +} + +int main(int argc, char* argv[]) +{ +#ifndef HACK_DONT_CHECK_FB + char* vidmode = GetCurrentVideoMode(); + if ( !vidmode ) { perror("Cannot detect current video mode"); exit(1); } + char* widthstr = NULL; + char* heightstr = NULL; + if ( !ReadParamString(vidmode, "width", &widthstr, "height", &heightstr, NULL) ) + { + error(1, errno, "Can't parse video mode: %s", vidmode); + } + xres = atoi_safe(widthstr); delete[] widthstr; widthstr = NULL; + yres = atoi_safe(heightstr); delete[] heightstr; heightstr = NULL; + if ( !xres || !yres ) + { + const char* chvideomode = "chvideomode"; + execlp(chvideomode, chvideomode, argv[0], NULL); + perror(chvideomode); + exit(127); + } +#else + xres = 1280; + yres = 720; +#endif + + fb = open("/dev/video/fb", O_WRONLY); +#ifndef HACK_DONT_CHECK_FB + if ( fb < 0 ) { error(1, errno, "open: /dev/video/fb"); } +#endif + bpp = sizeof(uint32_t); + linesize = xres; + framesize = yres * linesize * bpp; + buf = new uint32_t[framesize / sizeof(uint32_t)]; + InitGame(); + gamerunning = true; + for ( framenum = 0; gamerunning; framenum++ ) + { + RunFrame(); + } + close(fb); + return 0; +} diff --git a/libmaxsi/random.cpp b/libmaxsi/random.cpp index 2ed980b6..09256583 100644 --- a/libmaxsi/random.cpp +++ b/libmaxsi/random.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. This file is part of LibMaxsi. @@ -11,8 +11,8 @@ LibMaxsi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for - more details. + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. You should have received a copy of the GNU Lesser General Public License along with LibMaxsi. If not, see . @@ -20,7 +20,7 @@ random.cpp Provides access to random numbers using various algorithms. -******************************************************************************/ +*******************************************************************************/ #include @@ -28,12 +28,27 @@ namespace Maxsi { namespace Random { +#if 1 + unsigned int m_w = 1337; + unsigned int m_z = 37; + unsigned int RandomUnsignedInt() + { + m_z = 36969 * (m_z & 65535) + (m_z >> 16); + m_w = 18000 * (m_w & 65535) + (m_w >> 16); + return (m_z << 16) + m_w; /* 32-bit result */ + } + extern "C" int rand() + { + return RandomUnsignedInt() % 32768; + } +#else unsigned random_seed = 1337; extern "C" int rand() { random_seed = random_seed + 37 * 1103515245 + 12345; return random_seed >> 16; } +#endif } }