// Genesaver: copyright 2003 Sam Stafford. #define _USE_MATH_DEFINES #include <math.h> #include <stdlib.h> #include <stdio.h> #include <time.h> #include <GL/glut.h> #include "globals.h" #include "Brain.h" #include "DNA.h" #include "Thing.h" #include "Plant.h" #include "Animal.h" #include "World.h" World::World(void) { srand( time( 0 ) ); ptimer = graphcount = rgcount = 0; animals = NULL; plants = NULL; tagged = NULL; r = rc = g = gc = b = bc = p = 0.0; graphhead = graphtail = NULL; } World::~World(void) { } void World::CamChange() { glClear( GL_COLOR_BUFFER_BIT ); switch( settings.camera ) { case C_WORLD: settings.camera = C_CHASE; return; case C_GRAPH: settings.camera = C_WORLD; return; case C_CHASE: settings.camera = C_GRAPH; int rc = rgcount; rgcount = 1; RenderGraph(); rgcount = rc; } } void World::CheckCollisions( Animal* a ) { a->brain.Clear( a->energy ); a->brain.input[8].axon = Conflict( a ) ? 1.0 : 0.0 ; float dx, dy, r2; Plant* q; for ( Plant* p = plants; p; p = q ) { dx = p->XNearest( a->x ) - a->x; dy = p->YNearest( a->y ) - a->y; r2 = dx * dx + dy * dy; q = p->next; //because we might delete p if ( r2 < ( A_R + P_R ) * ( A_R + P_R ) ) Collide( a, p ); else if ( r2 < A_V * A_V ) { float pa; if ( dx == 0 && dy > 0 ) { if ( dy > 0 ) pa = M_PI / 2; else if ( dy < 0 ) pa = -M_PI / 2; else pa = 0; } else if ( dy == 0 ) { if ( dx > 0 ) pa = 0; else pa = M_PI; } else if ( dx > 0 ) { pa = atan( dy / dx ); } else if ( dx < 0 ) { if ( dy > 0 ) pa = atan( dy / dx ) + M_PI; else pa = atan( dy / dx ) - M_PI; } a->See( r2, P_C, 0, pa ); } } Animal* c; for ( Animal* b = animals; b; b = c ) { dx = b->XNearest( a->x ) - a->x; dy = b->YNearest( a->y ) - a->y; r2 = dx * dx + dy * dy; c = b->next; if ( a == b ) continue; if ( r2 < ( A_R + A_R ) * ( A_R + A_R ) ) Collide( a, b ); else if ( r2 < A_V * A_V ) { float pa; if ( dx == 0 && dy > 0 ) { if ( dy > 0 ) pa = M_PI / 2; else if ( dy < 0 ) pa = -M_PI / 2; else pa = 0; } else if ( dy == 0 ) { if ( dx > 0 ) pa = 0; else pa = M_PI; } else if ( dx > 0 ) { pa = atan( dy / dx ); } else if ( dx < 0 ) { if ( dy > 0 ) pa = atan( dy / dx ) + M_PI; else pa = atan( dy / dx ) - M_PI; } a->See( r2, b->color, b->diet, pa ); } } } void World::Clone( Animal* a ) { a->energy -= A_E; a->fighting = A_C; DNA* dna = new DNA( a->dna ); InsertAnimal( dna ); animals->x = a->x; animals->y = a->y; animals->angle = a->angle + 1.55; } void World::Collide( Animal* c, Animal* h ) { if ( c->color == h->color ) { Mate( c, h ); return; } else if ( ( c->color == Red && h->color == Green ) || ( c->color == Green && h->color == Blue ) || ( c->color == Blue && h->color == Red ) ) { if ( c->achewing || c->diet <= h->diet / 3.0 ) return; c->achewing = A_C; c->energy += A_E * A_D * ( c->diet - h->diet / 3.0 ); h->energy -= A_E * ( c->diet - h->diet / 3.0 ); if ( h->tagged && h->energy < 0.0 ) ViewChange( c ); return; } } void World::Collide( Animal* a, Plant* p ) { if ( a->pchewing ) return; a->pchewing = A_C; a->energy += P_E * A_D * ( 1.0 - a->diet ) ; p->energy -= P_E * ( 1.0 - a->diet ); if ( p->energy > 0.0 ) return; this->p--; if ( p == plants ) plants = p->next; if ( p->next ) p->next->prev = p->prev; if ( p->prev ) p->prev->next = p->next; delete p; } bool World::Conflict( Animal* a ) { switch( a->color ) { case Red: return rc * 3 > g; case Green: return gc * 3 > b; case Blue: default: return bc * 3 > r; } } void World::InsertAnimal( DNA* dna ) { if ( !animals ) { animals = new Animal( dna ); } else { animals->prev = new Animal( dna ); animals->prev->next = animals; animals = animals->prev; } switch( animals->color ) { case Red: r++; rc += animals->diet; break; case Green: g++; gc += animals->diet; break; case Blue: b++; bc += animals->diet; break; } } bool World::Kill( Animal* x, Animal* y ) { if ( x->fighting > 0 || !Conflict( x ) || x->diet * y->diet < 0.0001 ) return false; x->fighting = A_C; y->fighting = A_C; if ( x->energy > y->energy ) y->energy -= A_E * x->diet * y->diet * 1.25; else x->energy -= A_E * x->diet * y->diet * 1.25; return true; } void World::Load( char* c ) { //inject one extra creature into the world, just for fun DNA* dna = new DNA(); dna->Randomize(); while ( *c && *c != EOF ) { InsertAnimal( dna ); animals->x = RandFloat() * 2.0 - 1.0; animals->y = RandFloat() * 2.0 - 1.0; animals->angle = RandFloat() * 6; dna = new DNA(); c = dna->Load( c ); } delete dna; return; } void World::Mate( Animal* a, Animal* b ) { //Population pressure can cause animals of the same species to //kill each other rather than mate, especially carnivores. if ( Kill( a, b ) ) return; if ( a->energy + b->energy < A_E * 3.0 ) return; a->energy -= A_E / 2.0; b->energy -= A_E / 2.0; a->mating = A_C; b->mating = A_C; DNA* dna = new DNA( a->dna, b->dna ); InsertAnimal( dna ); animals->x = a->x; animals->y = a->y; animals->angle = (a->angle + b->angle ) / 2.0 + 1.55; } void World::Remove( Animal* a ) { if ( a == tagged ) tagged = NULL; switch( a->color ) { case Red: r--; rc -= a->diet; break; case Green: g--; gc -= a->diet; break; case Blue: b--; bc -= a->diet; break; } if ( a == animals ) animals = a->next; if ( a->next ) a->next->prev = a->prev; if ( a->prev ) a->prev->next = a->next; delete a; } bool World::RenderGraph() { if ( rgcount != 1 ) return false; GraphNode* foo; float gx = -1.0; glClear( GL_COLOR_BUFFER_BIT ); glBegin( GL_LINE_STRIP ); SetColor( Red ); for ( foo = graphhead; foo; foo = foo->next ) { if ( settings.shade ) SetColor21( Red, foo->rc / foo->r ); glVertex2f( gx, foo->r / foo->hpop - 1.0 ); gx += G_STEP; } glEnd(); gx = -1.0; glBegin( GL_LINE_STRIP ); SetColor( Green ); for ( foo = graphhead; foo; foo = foo->next ) { if ( settings.shade ) SetColor21( Green, foo->gc / foo->g ); glVertex2f( gx, foo->g / foo->hpop - 1.0 ); gx += G_STEP; } glEnd(); gx = -1.0; glBegin( GL_LINE_STRIP ); SetColor( Blue ); for ( foo = graphhead; foo; foo = foo->next ) { if ( settings.shade ) SetColor21( Blue, foo->bc / foo->b ); glVertex2f( gx, foo->b / foo->hpop - 1.0 ); gx += G_STEP; } glEnd(); return true; } void World::RenderTagged() { glClear( GL_COLOR_BUFFER_BIT ); if ( !tagged ) ViewChange(); if ( !tagged ) return; if ( settings.grid ) { glPushMatrix(); glTranslatef( 0, 0.5, 0 ); //move to top of screen glScalef( 0.5 / A_V, 0.5 / A_V, 0.5 / A_V ); //scale to vis range glTranslatef( -(tagged->x), -(tagged->y), 0 ); //center on target creature RenderGrid(); glPopMatrix(); } Plant* p = plants; while ( p ) { p->RenderBy( tagged->x, tagged->y ); p = p->next; } Animal* a = animals; while ( a ) { a->RenderBy( tagged->x, tagged->y ); a = a->next; } tagged->RenderEnergy(); tagged->brain.Render(); } void World::Render() { if ( settings.trail <= 1 ) { glClear( GL_COLOR_BUFFER_BIT ); } else { glColor4f( 0, 0, 0, fsettings.ta ); glEnable( GL_BLEND ); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin( GL_QUADS ); glVertex2f( -1, -1 ); glVertex2f( -1, 1 ); glVertex2f( 1, 1 ); glVertex2f( 1, -1 ); glEnd(); } glPushMatrix(); if ( settings.zoom >= fsettings.whratio ) glScalef( settings.zoom / fsettings.whratio, settings.zoom / 100.0, 1 ); else glScalef( settings.zoom / 100.0, settings.zoom / 100.0, 1 ); if ( settings.grid ) { RenderGrid(); } Plant* p = plants; while ( p ) { p->Render(); p = p->next; } Animal* a = animals; while ( a ) { a->Render(); a = a->next; } glPopMatrix(); } void World::Save( FILE *f ) { Animal* a; for ( a = animals ; a ; a = a->next ) a->dna->Dump( f ); } void World::SpawnAnimal() { float x = RandFloat() * 2.0 - 1.0; float y = RandFloat() * 2.0 - 1.0; float angle = RandFloat() * 3.14; DNA* dna = new DNA(); dna->Randomize(); InsertAnimal( dna ); animals->x = x; animals->y = y; animals->angle = angle; } void World::SpawnPlant() { float x = RandFloat() * 2.0 - 1.0; float y = RandFloat() * 2.0 - 1.0; if ( !plants ) { plants = new Plant(); } else { plants->prev = new Plant(); plants->prev->next = plants; plants = plants->prev; } p++; plants->x = x; plants->y = y; } void World::Step() { ptimer += P_S; for ( ; ptimer >= 1.0 ; ptimer -= 1.0 ) SpawnPlant(); if ( !animals ) { for ( int i = 0 ; i < 40 ; i++ ) SpawnAnimal(); } if ( RandFloat() < .00001 ) SpawnAnimal(); //spontaneous generation Animal* a, *b; for ( a = animals ; a ; a = a->next ) { CheckCollisions( a ); a->Step(); if ( a->energy >= A_E * 2.0 ) Clone( a ); if ( a->energy <= 0 ) a->dead = true; } for ( a = animals; a ; a = b ) { b = a->next; if ( a->dead ) Remove( a ); } UpdateGraph(); } void World::UpdateGraph() { if ( rgcount ) { rgcount++; if ( rgcount > G_TIME ) rgcount = 0; return; } rgcount++; if ( !graphhead ) { graphtail = new GraphNode(); graphhead = graphtail; } else { GraphNode* gn = new GraphNode(); graphtail->next = gn; graphtail = gn; } graphtail->next = NULL; graphtail->r = this->r; graphtail->g = this->g; graphtail->b = this->b; graphtail->rc = this->rc; graphtail->gc = this->gc; graphtail->bc = this->bc; graphtail->hpop = ( r + g + b ) / 2.0; graphcount++; while ( graphcount > 2.0 / G_STEP + 1.0 ) { GraphNode* hn = graphhead; graphhead = graphhead->next; delete hn; graphcount--; } } void World::ViewChange( Animal* a ) { if ( a ) { if ( tagged ) tagged->tagged = false; tagged = a; tagged->tagged = true; return; } if ( !tagged ) { tagged = animals; } else { tagged->tagged = false; tagged = tagged->next; } if ( tagged ) { tagged->tagged = true; } } void World::RenderGrid() { glDisable( GL_BLEND ); glBegin( GL_LINES ); glLineWidth( 1 ); glColor3f( 0.15, 0.15, 0.15 ); for ( float x = -0.9 ; x < 1.0 ; x += 0.1 ) { glVertex2f( x, -1.0 ); glVertex2f( x, 1.0 ); } for ( float y = -0.9 ; y < 1.0 ; y += 0.1 ) { glVertex2f( -1.0, y ); glVertex2f( 1.0, y ); } glEnd(); if ( settings.AA ) glEnable( GL_BLEND ); }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#8 | 5393 | Sam Stafford |
Integrate in Marc's porting changes, and merge my own relevant winmain.cpp changes into main.cpp. (Thanks Marc!!!) |
||
#7 | 5384 | Sam Stafford |
Increase maximum allowable creature speed, fix a couple of bugs (some of them balance-related) with the alternate ecology ruleset. |
||
#6 | 5337 | Sam Stafford | Blur trails. | ||
#5 | 5090 | Sam Stafford | Option to draw a grid on the background (they appear in both world and chase view), set by the "gridlines" option in the settings file. | ||
#4 | 4426 | Sam Stafford |
Seed randomizer at start of each run with current time. This prevents determinism (I noticed that if I turned off gene banking, I'd still get the same swarm of critters each time, because the simulation was using the same seed each time... oops). |
||
#3 | 4425 | Sam Stafford |
Fixed bug with graph - graph lines would stop updating if that population hit zero, and be permanently offset as a result. The line will now happily continue along if a given color population goes extinct. |
||
#2 | 3356 | Sam Stafford |
Calculate display width/height ratio, if possible, and set default zoom level to prevent distortion. (Windows only, thus far.) |
||
#1 | 3052 | Sam Stafford |
Add Genesaver to the Public Depot. It's not in any way Perforce-related, but it does share a bit of code with Jamgraph, and it feels strange to have an open-source project that's not in the PD. |