diff --git a/Evagents.vcproj b/Evagents.vcproj index dc3c683..ed14c92 100644 --- a/Evagents.vcproj +++ b/Evagents.vcproj @@ -213,7 +213,7 @@ > using namespace std; -Agent::Agent(int NUMBOXES, float MEANRADIUS, float REP_PER_BABY, float MUTARATE1, float MUTARATE2) : - brain(NUMBOXES) -{ +Agent::Agent( + int NUMBOXES, + int NUMINITCONNS, + bool SPAWN_MIRROR_EYES, + int OVERRIDE_KINRANGE, + float MEANRADIUS, + float REP_PER_BABY, + float BRAIN_MUTATION_CHANCE, + float BRAIN_MUTATION_SIZE, + float GENE_MUTATION_CHANCE, + float GENE_MUTATION_SIZE +){ //basics id= 0; setPosRandom(conf::WIDTH, conf::HEIGHT); angle= randf(-M_PI,M_PI); //genes - MUTCHANCE= abs(MUTARATE1+randf(-conf::META_MUTCHANCE,conf::META_MUTCHANCE)); //chance of mutations. - MUTSIZE= abs(MUTARATE2+randf(-conf::META_MUTSIZE,conf::META_MUTSIZE)*10); //size of mutations + brain_mutation_chance= BRAIN_MUTATION_CHANCE; //abs(BRAIN_MUTATION_CHANCE+randf(-conf::META_MUTCHANCE,conf::META_MUTCHANCE)); + brain_mutation_size= BRAIN_MUTATION_SIZE; //abs(MUTARATE2+randf(-conf::META_MUTSIZE,conf::META_MUTSIZE)*10); + gene_mutation_chance= GENE_MUTATION_CHANCE; //abs(BRAIN_MUTATION_CHANCE+randf(-conf::META_MUTCHANCE,conf::META_MUTCHANCE)); + gene_mutation_size= GENE_MUTATION_SIZE; //abs(MUTARATE2+randf(-conf::META_MUTSIZE,conf::META_MUTSIZE)*10); parentid= 0; radius= randf(MEANRADIUS*0.2,MEANRADIUS*2.2); + strength= randf(0.01,1); numbabies= randi(1,7); gene_red= randf(0,1); gene_gre= randf(0,1); @@ -26,18 +38,25 @@ Agent::Agent(int NUMBOXES, float MEANRADIUS, float REP_PER_BABY, float MUTARATE1 metabolism= randf(0.25,0.75); setIdealTempPref(); species= randi(-conf::SPECIESID_RANGE,conf::SPECIESID_RANGE); - kinrange= randi(1,conf::SPECIESID_RANGE/10); - sexprojectbias= randf(-1,0); //purposefully excluding "male" biases (0,1), which are allowed, but for random spawn agents are detrimental + kinrange= OVERRIDE_KINRANGE>=0 ? OVERRIDE_KINRANGE : randi(1,conf::SPECIESID_RANGE/10); + sexprojectbias= -1; //purposefully excluding "male" and "female" biases for random spawn agents setRandomStomach(); //senses and sense-ability //eyes - eye_see_agent_mod= randf(0.1, 3); + eye_see_agent_mod= randf(0.3, 3); + eye_see_cell_mod= randf(0.3, 3); eyefov.resize(NUMEYES, 0); eyedir.resize(NUMEYES, 0); for(int i=0;itemp2){ @@ -57,6 +82,7 @@ Agent::Agent(int NUMBOXES, float MEANRADIUS, float REP_PER_BABY, float MUTARATE1 hearhigh[i]= temp2; } } + clockf1= randf(5,100); clockf2= randf(5,100); clockf3= 5; @@ -64,6 +90,7 @@ Agent::Agent(int NUMBOXES, float MEANRADIUS, float REP_PER_BABY, float MUTARATE1 smell_mod= randf(0.1, 3); //brain matters + brain = CPBrain(NUMBOXES, NUMINITCONNS); in.resize(Input::INPUT_SIZE, 0); out.resize(Output::OUTPUT_SIZE, 0); brainmutations= 0; @@ -87,7 +114,6 @@ Agent::Agent(int NUMBOXES, float MEANRADIUS, float REP_PER_BABY, float MUTARATE1 //output mechanical values w1= 0; w2= 0; - strength= randf(0.01,1); boost= false; jump= 0; real_red= 0.5; @@ -137,15 +163,252 @@ Agent::Agent(){ //} +Agent Agent::reproduce( + Agent that, + bool PRESERVE_MIRROR_EYES, + int OVERRIDE_KINRANGE, + float MEANRADIUS, + float REP_PER_BABY, + int baby +){ + //moved muterate gets into reproduce because thats where its needed and used, no reason to go through world + //choose a value of our agent's mutation and their saved mutation value, can be anywhere between the parents' + float BMR= randf(min(this->brain_mutation_chance, that.brain_mutation_chance), max(this->brain_mutation_chance, that.brain_mutation_chance)); + float BMR2= randf(min(this->brain_mutation_size, that.brain_mutation_size), max(this->brain_mutation_size, that.brain_mutation_size)); + float GMR= randf(min(this->gene_mutation_chance, that.gene_mutation_chance), max(this->gene_mutation_chance, that.gene_mutation_chance)); + float GMR2= randf(min(this->gene_mutation_size, that.gene_mutation_size), max(this->gene_mutation_size, that.gene_mutation_size)); + + //create baby. Note that if the bot selects itself to mate with, this function acts also as assexual reproduction + //NOTES: Agent "this" is mother, Agent "that" is father, Agent "a2" is daughter + //if a single parent's trait is required, use the mother's (this->) + Agent a2( + this->brain.boxes.size(), + this->brain.conns.size(), + PRESERVE_MIRROR_EYES, + OVERRIDE_KINRANGE, + MEANRADIUS, + this->maxrepcounter, + BMR, + BMR2, + GMR, + GMR2 + ); + //Agent::Agent( + + //spawn the baby somewhere closeby behind the mother + //we want to spawn behind so that agents dont accidentally kill their young right away + //note that this relies on bots actally driving forward, not backward. We'll let natural selection choose who lives and who dies + Vector2f fb(this->radius*randf(1.9, 2.5),0); + float floatrange = 0.4; + fb.rotate(this->angle + M_PI*(1 + floatrange - 2*floatrange*(baby+1)/(this->numbabies+1))); + a2.pos= this->pos + fb; + a2.dpos= a2.pos; + a2.borderRectify(); + + //basic trait inheritance + a2.gencount= max(this->gencount+1,that.gencount+1); + a2.numbabies= randf(0,1)<0.5 ? this->numbabies : that.numbabies; + a2.metabolism= randf(0,1)<0.5 ? this->metabolism : that.metabolism; + for(int i=0; istomach[i]: that.stomach[i]; + a2.species= randf(0,1)<0.5 ? this->species : that.species; + a2.kinrange= randf(0,1)<0.5 ? this->kinrange : that.kinrange; + a2.radius= randf(0,1)<0.5 ? this->radius : that.radius; + a2.strength= randf(0,1)<0.5 ? this->strength : that.strength; + a2.chamovid= randf(0,1)<0.5 ? this->chamovid : that.chamovid; + a2.gene_red= randf(0,1)<0.5 ? this->gene_red : that.gene_red; + a2.gene_gre= randf(0,1)<0.5 ? this->gene_gre : that.gene_gre; + a2.gene_blu= randf(0,1)<0.5 ? this->gene_blu : that.gene_blu; + a2.sexprojectbias= randf(0,1)<0.5 ? this->sexprojectbias : that.sexprojectbias; + + a2.brain_mutation_chance= randf(0,1)<0.5 ? this->brain_mutation_chance : that.brain_mutation_chance; + a2.brain_mutation_size= randf(0,1)<0.5 ? this->brain_mutation_size : that.brain_mutation_size; + a2.parentid= this->id; //parent ID is strictly inherited from mothers + a2.clockf1= randf(0,1)<0.5 ? this->clockf1 : that.clockf1; + a2.clockf2= randf(0,1)<0.5 ? this->clockf2 : that.clockf2; + + a2.smell_mod= randf(0,1)<0.5 ? this->smell_mod : that.smell_mod; + a2.hear_mod= randf(0,1)<0.5 ? this->hear_mod : that.hear_mod; + a2.eye_see_agent_mod= randf(0,1)<0.5 ? this->eye_see_agent_mod : that.eye_see_agent_mod; + a2.eye_see_cell_mod= randf(0,1)<0.5 ? this->eye_see_cell_mod : that.eye_see_cell_mod; + a2.blood_mod= randf(0,1)<0.5 ? this->blood_mod : that.blood_mod; + + a2.temperature_preference= randf(0,1)<0.5 ? this->temperature_preference : that.temperature_preference; + a2.discomfort= this->discomfort; + a2.lungs= randf(0,1)<0.5 ? this->lungs : that.lungs; + + for (int i=0; ieardir[i]; + a2.hearlow[i]= this->hearlow[i]; + a2.hearhigh[i]= this->hearhigh[i]; + } else { + a2.eardir[i]= that.eardir[i]; + a2.hearlow[i]= that.hearlow[i]; + a2.hearhigh[i]= that.hearhigh[i]; + } + } + + for (int i=0; ieyefov[i]; + a2.eyedir[i]= this->eyedir[i]; + } else { + a2.eyefov[i]= that.eyefov[i]; + a2.eyedir[i]= that.eyedir[i]; + } + } + + //---MUTATIONS---// + if (randf(0,1)M_PI) a2.eyefov[i] = M_PI; //eyes cannot wrap around agent + + if(randf(0,1)2*M_PI) a2.eyedir[i] = 2*M_PI; + //not going to loop coordinates; 0,2pi is agents' front again, so it provides a good point to "bounce" off of + } + else { + eyedir[i]= 2*M_PI - eyedir[i-1]; + eyefov[i] = eyefov[i-1]; + } + } + + for(int i=0;i2*M_PI) a2.eardir[i] = 2*M_PI; + } else { //IMPLEMENT WORLD SETTING FOR CONTROLLING THIS + eardir[i]= eardir[i-1]; + } + + if(randf(0,1)a2.hearhigh[i]) { + float temp= a2.hearlow[i]; + a2.hearlow[i]= a2.hearhigh[i]; + a2.hearhigh[i]= temp; + } + } + + //create brain here + a2.brain= this->brain.crossover(that.brain); + a2.brain.initMutate(BMR,BMR2); + + a2.initSplash(conf::RENDER_MAXSPLASHSIZE*0.5,0.8,0.8,0.8); //grey event means we were just born! Welcome! + + return a2; +} + +void Agent::liveMutate(int MUTMULT) +{ + initSplash(conf::RENDER_MAXSPLASHSIZE*0.75,0.5,0,1.0); + + float BMR= this->gene_mutation_chance*MUTMULT; + float BMR2= this->gene_mutation_size; + float GMR= this->gene_mutation_chance*MUTMULT; + float GMR2= this->gene_mutation_size; + for(int i= 0; ibrain.liveMutate(BMR, BMR2, this->out); + } + + //change other live-mutable traits here + if (randf(0,1)metabolism= cap(randn(this->metabolism, GMR2/5)); + for(int i=0; istomach[i]= cap(randn(this->stomach[i], GMR2)); + if (randf(0,1)brain_mutation_chance= abs(randn(this->brain_mutation_chance, conf::META_MUTSIZE*20)); + if (randf(0,1)brain_mutation_size= abs(randn(this->brain_mutation_size, conf::META_MUTSIZE/2)); + if (randf(0,1)gene_mutation_chance= abs(randn(this->gene_mutation_chance, conf::META_MUTSIZE*20)); + if (randf(0,1)gene_mutation_size= abs(randn(this->gene_mutation_size, conf::META_MUTSIZE/2)); + //sensory failure: rare chance that the value of a sense gets cut down by roughly half, depending on mutation size + if (randf(0,1)smell_mod /= randf(1, 2+GMR2*50); + if (randf(0,1)hear_mod /= randf(1, 2+GMR2*50); + if (randf(0,1)eye_see_agent_mod /= randf(1, 2+GMR2*50); + if (randf(0,1)eye_see_cell_mod /= randf(1, 2+GMR2*50); + if (randf(0,1)blood_mod /= randf(1, 2+GMR2*50); + if (randf(0,1)clockf1= randn(this->clockf1, GMR2/2); + if (this->clockf1<2) this->clockf1= 2; + if (randf(0,1)clockf2= randn(this->clockf2, GMR2/2); + if (this->clockf2<2) this->clockf2= 2; + if (randf(0,1)temperature_preference= cap(randn(this->temperature_preference, GMR2/4)); +} + +void Agent::tick() +{ + brain.tick(in, out); +} + void Agent::printSelf() { - printf("\n~~~~~~~~~~~~~~~~~~~~~~~~~\n"); + printf("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); //print all Variables //bot basics printf("AGENT, ID: %i\n", id); printf("pos & angle: (%f,%f), %f\n", pos.x, pos.y, angle); printf("health, age, & gencount: %f, %.1f, %i\n", health, (float)age/10, gencount); - printf("MUTCHANCE: %f, MUTSIZE: %f\n", MUTCHANCE, MUTSIZE); + printf("brain_mutation_chance: %f, brain_mutation_size: %f\n", brain_mutation_chance, brain_mutation_size); printf("parent ID: &i\n", parentid); printf("radius: %f\n", radius); printf("strength: %f\n", strength); @@ -153,9 +416,11 @@ void Agent::printSelf() printf("gene_red: %f\ngene_gre: %f\ngene_blu: %f\n", gene_red, gene_gre, gene_blu); //triggers, counters, and layer interaction + printf("====== triggers, counters, and layer interaction ======\n"); if(near) printf("PRESENTLY NEAR\n"); else printf("PRESENTLY NOT NEAR (not interaction-processed)\n"); printf("freshkill: %i\n", freshkill); + printf("carcasscount: %i\n", carcasscount); printf("species id: %i\n", species); printf("kin_range: %i\n", kinrange); printf("num_babies: %f\n", numbabies); @@ -177,47 +442,50 @@ void Agent::printSelf() else printf("not encumbered\n"); //senses -// float eye_see_agent_mod; + printf("======================= senses ========================\n"); + printf("eye_see_agent_mod: %f\n", eye_see_agent_mod); + printf("eye_see_cell_mod: %f\n", eye_see_cell_mod); // std::vector eyefov; //field of view for each eye // std::vector eyedir; //direction of each eye -// float hear_mod; + printf("hear_mod: %f\n", hear_mod); // std::vector eardir; //position of ears // std::vector hearlow; //low values of hearing ranges // std::vector hearhigh; //high values of hearing ranges -// float clockf1, clockf2, clockf3; //the frequencies of the three clocks of this bot -// float blood_mod; -// float smell_mod; + printf("clockf1: %f, clockf2: %f, clockf3: %f\n", clockf1, clockf2, clockf3); + printf("blood_mod: %f\n", blood_mod); + printf("smell_mod: %f\n", smell_mod); //the brain + printf("====================== the brain ======================\n"); printf("brain mutations: %f\n", brainmutations); for(int i=0; i=Input::EYES && inputs[i]<=Input::xEYES){ - printf("eye#%d_",(int)floor((float)(inputs[i])/3.0)); + printf("eye#%d_",(int)floor((float)(inputs[i])/3.0)-Input::EYES); if(inputs[i]%3==0) printf("red"); //a->in[Input::EYES+i*3]= cap(r[i]); else if(inputs[i]%3==1) printf("green"); //a->in[Input::EYES+i*3+1]= cap(g[i]); else if(inputs[i]%3==2) printf("blue"); //a->in[Input::EYES+i*3+2]= cap(b[i]); + } else if(inputs[i]>=Input::EARS && inputs[i]<=Input::xEARS){ + printf("ear#%d",inputs[i]-Input::EARS); } printf(" "); } - printf("\n\n"); + printf("\n\n");*/ } @@ -339,185 +609,12 @@ float Agent::getOutputSum() const return sum; } -void Agent::tick() -{ - brain.tick(in, out); -} -Agent Agent::reproduce(Agent that, float MEANRADIUS, float REP_PER_BABY) -{ - //moved muterate gets into reproduce because thats where its needed and used, no reason to go through world - //choose a value of our agent's mutation and their saved mutation value, can be anywhere between the parents' - float MR= randf(min(this->MUTCHANCE,that.MUTCHANCE),max(this->MUTCHANCE,that.MUTCHANCE)); - float MR2= randf(min(this->MUTSIZE,that.MUTSIZE),max(this->MUTSIZE,that.MUTSIZE)); - - //create baby. Note that if the bot selects itself to mate with, this function acts also as assexual reproduction - //NOTES: Agent "this" is mother, Agent "that" is father, Agent "a2" is daughter - //if a single parent's trait is required, use the mother's (this->) - Agent a2(this->brain.boxes.size(), MEANRADIUS, this->maxrepcounter, MR, MR2); - - //spawn the baby somewhere closeby behind the mother - //we want to spawn behind so that agents dont accidentally kill their young right away - //note that this relies on bots actally driving forward, not backward. We'll let natural selection choose who lives and who dies - Vector2f fb(this->radius*randf(2, 3),0); - fb.rotate(this->angle+M_PI+this->numbabies*randf(-0.4,0.4)); - a2.pos= this->pos + fb; - a2.dpos= a2.pos; - a2.borderRectify(); - - //basic trait inheritance - a2.gencount= max(this->gencount+1,that.gencount+1); - a2.numbabies= randf(0,1)<0.5 ? this->numbabies : that.numbabies; - a2.metabolism= randf(0,1)<0.5 ? this->metabolism : that.metabolism; - for(int i=0; istomach[i]: that.stomach[i]; - a2.species= randf(0,1)<0.5 ? this->species : that.species; - a2.kinrange= randf(0,1)<0.5 ? this->kinrange : that.kinrange; - a2.radius= randf(0,1)<0.5 ? this->radius : that.radius; - a2.strength= randf(0,1)<0.5 ? this->strength : that.strength; - a2.chamovid= randf(0,1)<0.5 ? this->chamovid : that.chamovid; - a2.gene_red= randf(0,1)<0.5 ? this->gene_red : that.gene_red; - a2.gene_gre= randf(0,1)<0.5 ? this->gene_gre : that.gene_gre; - a2.gene_blu= randf(0,1)<0.5 ? this->gene_blu : that.gene_blu; - a2.sexprojectbias= randf(0,1)<0.5 ? this->sexprojectbias : that.sexprojectbias; - - a2.MUTCHANCE= randf(0,1)<0.5 ? this->MUTCHANCE : that.MUTCHANCE; - a2.MUTSIZE= randf(0,1)<0.5 ? this->MUTSIZE : that.MUTSIZE; - a2.parentid= this->id; //parent ID is strictly inherited from mothers - a2.clockf1= randf(0,1)<0.5 ? this->clockf1 : that.clockf1; - a2.clockf2= randf(0,1)<0.5 ? this->clockf2 : that.clockf2; - - a2.smell_mod= randf(0,1)<0.5 ? this->smell_mod : that.smell_mod; - a2.hear_mod= randf(0,1)<0.5 ? this->hear_mod : that.hear_mod; - a2.eye_see_agent_mod= randf(0,1)<0.5 ? this->eye_see_agent_mod : that.eye_see_agent_mod; -// a2.eye_see_cell_mod= randf(0,1)<0.5 ? this->eye_see_cell_mod : that.eye_see_cell_mod; - a2.blood_mod= randf(0,1)<0.5 ? this->blood_mod : that.blood_mod; - - a2.temperature_preference= randf(0,1)<0.5 ? this->temperature_preference : that.temperature_preference; - a2.discomfort= this->discomfort; - a2.lungs= randf(0,1)<0.5 ? this->lungs : that.lungs; - - a2.eardir= randf(0,1)<0.5 ? this->eardir : that.eardir; - a2.hearlow= randf(0,1)<0.5 ? this->hearlow : that.hearlow; - a2.hearhigh= randf(0,1)<0.5 ? this->hearhigh : that.hearhigh; - - a2.eyefov= randf(0,1)<0.5 ? this->eyefov : that.eyefov; - a2.eyedir= randf(0,1)<0.5 ? this->eyedir : that.eyedir; - - - //---MUTATIONS---// - if (randf(0,1)M_PI) a2.eyefov[i] = M_PI; //eyes cannot wrap around bot - - if(randf(0,1)2*M_PI) a2.eyedir[i] = 2*M_PI; - //not going to loop coordinates; 0,2pi is bots front again, so it provides a good point to "bounce" off of - } - - for(int i=0;i2*M_PI) a2.eardir[i] = 2*M_PI; - if(randf(0,1)a2.hearhigh[i]) { - float temp= a2.hearlow[i]; - a2.hearlow[i]= a2.hearhigh[i]; - a2.hearhigh[i]= temp; - } - } - - //create brain here - a2.brain= this->brain.crossover(that.brain); - a2.brain.initMutate(MR,MR2); - - a2.initSplash(conf::RENDER_MAXSPLASHSIZE*0.5,0.8,0.8,0.8); //grey event means we were just born! Welcome! - - return a2; - -} - void Agent::resetRepCounter(float MEANRADIUS, float REP_PER_BABY) { - this->maxrepcounter= max(conf::REPCOUNTER_MIN, REP_PER_BABY*this->numbabies*sqrt(this->radius/MEANRADIUS)); //division and sqrt... + this->maxrepcounter= max(conf::REPCOUNTER_MIN, REP_PER_BABY*this->numbabies*sqrt(this->radius/MEANRADIUS)); this->repcounter= this->maxrepcounter; } -void Agent::liveMutate(int MUTMULT) -{ - initSplash(conf::RENDER_MAXSPLASHSIZE*0.5,0.5,0,1.0); - - float MR= this->MUTCHANCE*MUTMULT; - float MR2= this->MUTSIZE; - for(int i= 0; ibrain.liveMutate(MR, MR2, this->out); - } - - //change other mutable traits here - if (randf(0,1)metabolism= cap(randn(this->metabolism, MR2/5)); - for(int i=0; istomach[i]= cap(randn(this->stomach[i], MR2)); - if (randf(0,1)MUTCHANCE= abs(randn(this->MUTCHANCE, conf::META_MUTSIZE*20)); - if (randf(0,1)MUTSIZE= abs(randn(this->MUTSIZE, conf::META_MUTSIZE/2)); - if (randf(0,1)clockf1= randn(this->clockf1, MR2/2); - if (this->clockf1<2) this->clockf1= 2; - if (randf(0,1)clockf2= randn(this->clockf2, MR2/2); - if (this->clockf2<2) this->clockf2= 2; - if (randf(0,1)temperature_preference= cap(randn(this->temperature_preference, MR2/4)); -} - void Agent::setHerbivore() { this->stomach[Stomach::PLANT]= randf(0.5, 1); @@ -582,7 +679,13 @@ void Agent::setIdealTempPref(float temp) void Agent::setIdealLungs(float target) { - lungs= cap(randn(target,0.10)); + lungs= cap(randn(target,0.05)); +} + +bool Agent::isDead() const +{ + if (health<=0) return true; + return false; } bool Agent::isHerbivore() const @@ -621,9 +724,9 @@ bool Agent::isAquatic() const return false; } -bool Agent::isSpikey(float SPIKELENGTH) const +bool Agent::isSpikey(float SPIKE_LENGTH) const { - if(spikeLength*SPIKELENGTH>=radius) return true; + if(spikeLength*SPIKE_LENGTH>=radius) return true; return false; } @@ -651,6 +754,13 @@ bool Agent::isMale() const return false; } +int Agent::getRepType() const +{ + if(isAsexual()) return RepType::ASEXUAL; + else if(isMale()) return RepType::MALE; + return RepType::FEMALE; +} + bool Agent::isGrabbing() const { if(grabbing>0.5) return true; @@ -685,7 +795,7 @@ void Agent::addDamage(const char * sourcetext, float amount) void Agent::addDamage(std::string sourcetext, float amount) { //this method now handles subtraction of health as well as checking if the agent died - #pragma omp critical //protect us from ourselves... collapse any threads for + #pragma omp critical //protect us from ourselves... collapse any "for" threads if(amount>0){ this->health-= amount; @@ -707,6 +817,11 @@ void Agent::addDamage(std::string sourcetext, float amount) //check if agent died. If it did, properly bury it if(this->health<0){ + if(age < conf::TENDERAGE && (sourcetext.compare(conf::DEATH_HAZARD)==0 || sourcetext.compare(conf::DEATH_NATURAL)==0) && gencount > 0) { + sourcetext= std::string(conf::DEATH_TOOYOUNG); + //before TENDERAGE, a death is suspected to be caused by mutations + //only overwrite hazard and natural deaths however; I want to know when children die from murders, generocity, temperature, etc + } this->death= "Killed by " + sourcetext; this->health= 0; this->carcasscount= 0; diff --git a/SOURCE/Agent.h b/SOURCE/Agent.h index 9e71798..1edb459 100644 --- a/SOURCE/Agent.h +++ b/SOURCE/Agent.h @@ -1,7 +1,7 @@ #ifndef AGENT_H #define AGENT_H -#include "DRAWSBrain.h" +#include "CPBrain.h" #include "vmath.h" #include @@ -12,21 +12,35 @@ class Agent { //IMPORTANT: if ANY variables are added/removed, you MUST check ReadWrite.cpp to see how loading and saving will be effected!!! public: - Agent(int NUMBOXES, float MEANRADIUS, float REP_PER_BABY, float MUTARATE1, float MUTARATE2); + Agent( + int NUMBOXES, + int NUMINITCONNS, + bool SPAWN_MIRROR_EYES, + int OVERRIDE_KINRANGE, + float MEANRADIUS, + float REP_PER_BABY, + float BRAIN_MUTATION_CHANCE, + float BRAIN_MUTATION_SIZE, + float GENE_MUTATION_CHANCE, + float GENE_MUTATION_SIZE); Agent(); //Saved Variables //simulation basics int id; Vector2f pos; - Vector2f dpos; //UNSAVED + Vector2f dpos; //UNSAVED, but LOADED (with the pos values) + float height; //TODO float angle; //of the bot //Genes! WIP - std::vector > genes; //NEW genes. First is type, second is value. All Values of same Type get averaged or added + std::vector< std::pair > genes; //TODO: NEW genes. First is type, second is value. All Values of same Type get averaged or added + //REDO! make new structs instead. need more variables - float MUTCHANCE; //how often do mutations occur? - float MUTSIZE; //how significant are they? + float brain_mutation_chance; //how often do mutations occur? + float brain_mutation_size; //how significant are they? + float gene_mutation_chance; //same as above, but for genes + float gene_mutation_size; int parentid; //who's your momma? Note that like mitochondrial DNA, this is only passed from mother to children float radius; //radius of bot float gene_red, gene_gre, gene_blu; //genetic color traits of the agent. can be hidden by chamovid= 1 @@ -41,6 +55,7 @@ class Agent float sexprojectbias; //a physical bias trait, making sexual reproduction easier for some species/members. in range [0,1] //senses float eye_see_agent_mod; + float eye_see_cell_mod; std::vector eyefov; //field of view for each eye std::vector eyedir; //direction of each eye float hear_mod; @@ -50,8 +65,9 @@ class Agent float clockf1, clockf2, clockf3; //the frequencies of the three clocks of this bot float blood_mod; float smell_mod; + //the BRAIN!!! - DRAWSBrain brain; + CPBrain brain; std::vector in; //see Input in settings.h std::vector out; //see Output in settings.h @@ -89,7 +105,7 @@ class Agent std::vector mutations; std::vector> damages; //tracker for sources of injury std::vector> intakes; //tracker for sources of intake - std::string death; //the cause of death of this agent + std::string death; //the cause of death of this agent. Do not load-save without handling spaces //outputs @@ -128,7 +144,14 @@ class Agent void addIntake(std::string sourcetext, float amount); void writeIfKilled(); - Agent reproduce(Agent that, float MEANRADIUS, float REP_PER_BABY); + Agent reproduce( + Agent that, + bool PRESERVE_MIRROR_EYES, + int OVERRIDE_KINRANGE, + float MEANRADIUS, + float REP_PER_BABY, + int baby + ); void resetRepCounter(float MEANRADIUS, float REP_PER_BABY); void liveMutate(int MUTMULT= 1); @@ -145,17 +168,19 @@ class Agent void setIdealTempPref(float temp= -1); void setIdealLungs(float target); + bool isDead() const; bool isHerbivore() const; bool isCarnivore() const; bool isFrugivore() const; bool isTerrestrial() const; bool isAmphibious() const; bool isAquatic() const; - bool isSpikey(float SPIKELENGTH) const; + bool isSpikey(float SPIKE_LENGTH) const; bool isTiny() const; bool isTinyEye(int eyenumber) const; bool isAsexual() const; bool isMale() const; + int getRepType() const; bool isGrabbing() const; bool isGiving() const; bool isSelfish(float MAXSELFISH) const; diff --git a/SOURCE/CPBrain.cpp b/SOURCE/CPBrain.cpp new file mode 100644 index 0000000..794be20 --- /dev/null +++ b/SOURCE/CPBrain.cpp @@ -0,0 +1,603 @@ +#include "CPBrain.h" + +using namespace std; + +CPBox::CPBox(int numboxes) +{ + kp = cap(randf(0,1.3)); + gw = randf(-2,2); + bias = randn(0,1); + + acc = 0; + out = 0; + oldout = 0; + target = 0; + seed = 0; +} + +CPBox::CPBox(){ +} + +CPConn::CPConn(int numboxes) +{ + w = randn(0,5); + + if (randf(0,1) < conf::BRAIN_DIRECTINPUTS) sid = randi(-Input::INPUT_SIZE, 0); //connect a portion of the brain directly to input (negative sid). + else sid = randi(-Input::INPUT_SIZE, numboxes); + + tid = randi(max(0, min(sid + 1, numboxes - 1)), numboxes); //always connect forward in new brains, to emulate layering + + type = 0; + if(randf(0,1) < conf::BRAIN_CHANGECONNS) type = 1; //some conns can be change sensitive synapses +// if(randf(0,1) < conf::BRAIN_MEMCONNS) type = 2; //some conns can be memory synapses NOT IMPLEMENTED + seed = 0; + dead = true; +} + +CPConn::CPConn(){ +} + +CPBrain::CPBrain(){ +} + +CPBrain::CPBrain(int numboxes, int numconns) +{ + // do not OMP!!! + for (int i=0; i < numboxes; i++) { + CPBox a = CPBox(numboxes); + boxes.push_back(a); + } + // do not OMP!!! + for (int i=0; i < numconns; i++) { + CPConn a = CPConn(boxes.size()); + conns.push_back(a); + } + for (int i=0; i < numconns*conf::BRAIN_MIRRORCONNS; i++) { + CPConn a = CPConn(boxes.size()); + int randconnidx = randi(0,conns.size()); + a.w = -conns[randconnidx].w; + a.tid = conns[randconnidx].tid; + conns.push_back(a); + } + + resetBrain(); +} + +CPBrain& CPBrain::operator=(const CPBrain& other) +{ + if( this != &other ) { + boxes= other.boxes; + conns= other.conns; + } + return *this; +} + +void CPBrain::setLives() +{ + //all lives are set at init to t/f based on if in INPUT or OUTPUT ranges already, true if between the ranges, false if inside the ranges (always simulate) + //so all we need to do is set dead flag = false for all boxes used as a source for a target that is not dead +// #pragma omp parallel for + while (true) { + bool enlivenedconn = false; + + for (int i=0; i < (int)conns.size(); i++) { + if (!conns[i].dead) continue; + + int btid = boxRef(conns[i].tid); + if (btid >= ((int)(boxes.size()) - Output::OUTPUT_SIZE) || !boxes[btid].dead) { + if (conns[i].sid>=0) + boxes[boxRef(conns[i].sid)].dead = false; + conns[i].dead = false; + enlivenedconn = true; + } + } + + if (!enlivenedconn) break; + } +} + +//reset ALL boxes and conns, and set dead flags +void CPBrain::resetLives() +{ + int numboxes = (int)boxes.size(); + #pragma omp parallel for + for (int i=0; i < numboxes; i++) { + if(i >= numboxes - Output::OUTPUT_SIZE) boxes[i].dead = false; + else boxes[i].dead = true; + } + #pragma omp parallel for + for (int i=0; i < conns.size(); i++) { + conns[i].dead = true; + } + setLives(); +} + +//This method will update live lists to find and delete ALL dead conns (conns leading to dead boxes). Use while creating new agents +void CPBrain::resetBrain() +{ + //set live boxes' dead flag based on if in Inputs/Outputs, or if referenced as a source for a conn + resetLives(); + + //clean the connections which lead to dead boxes after the reset + vector::iterator iter= conns.begin(); + while (iter != conns.end() - 1) { //-1 so we never delete the last conn no matter what + if (iter->dead) { + iter= conns.erase(iter); + } else { + ++iter; + } + } + +// healthCheck(); +} + +void CPBrain::healthCheck() +{ + for (int i=0; i < (int)conns.size(); i++){ + conns[i].tid = boxRef(conns[i].tid); + } +// for (int i=0; i < (int)boxes.size(); i++){ +// printf("%i, %i\n", i, (int)boxes[i].dead); +// } +} + +int CPBrain::inRef(int id) +{ + if (id < 0) id = - id - 1; + int refval = capm(id, 0, Input::INPUT_SIZE - 1); + if (refval != id) { + printf("BRAIN ERROR: an input ID passed to 'inRef()' was invalid. Please set a breakpoint\n"); + } + return refval; +} + +int CPBrain::boxRef(int id) +{ + int refval = capm(id, 0, boxes.size() - 1); + if (refval != id) { + printf("BRAIN ERROR: a box ID passed to 'boxRef()' was invalid. Please set a breakpoint\n"); + } + return refval; +} + +int CPBrain::connRef(int id) +{ + int refval = capm(id, 0, conns.size() - 1); + if (refval != id) { + printf("BRAIN ERROR: a connection ID passed to 'connRef()' was invalid. Please set a breakpoint\n"); + } + return refval; +} + +//do a single tick of the brain, for each conn. Note that all conns are assumed live and lead to live boxes per "resetBrain()" +//DO NOT OMP anything, we handle that on the layer above, for each agent +void CPBrain::tick(vector< float >& in, vector< float >& out) +{ + for (int i=0; i < (int)conns.size(); i++){ + //first, get the source value. if -, it's an input; otherwise if +, it's a brain box + float value; + if (conns[i].sid < 0) value = in[inRef(conns[i].sid)]; + else value = boxes[boxRef(conns[i].sid)].out; + + if(conns[i].type == 1 && conns[i].sid >= 0){ //change sensitive conn compares to old value, and gets magnified by *100 (arbitrary) + value -= boxes[boxRef(conns[i].sid)].oldout; + value *= 100; + } + + //multiply by weight and add to the accumulation of the target box + boxes[boxRef(conns[i].tid)].acc += value*conns[i].w; + } + + //next, for all live boxes... + for (int i=0; i < (int)boxes.size(); i++) { + if (boxes[i].dead) continue; + + CPBox* box = &boxes[i]; //DO NOT De-Pointer-ize! It causes glitches! + + float presig = (box->acc + box->bias)*box->gw; + + //put value through fast sigmoid. very negative values -> 0, very positive values -> 1, values close to 0 -> near 0.5 + box->target = 0.5 * (presig / ( 1 + abs(presig)) + 1); + //box->target = 1.0 / (1.0 + exp( -(box->acc + box->bias)*box->gw )); //old sigmoid + + //done with acc, reset it, and back up current out for each box + box->acc = 0; + box->oldout = box->out; + + //make all boxes go a bit toward target, with dampening applied + box->out += (box->target - box->out) * box->kp; + + //finally, set out[] to the last few boxes to the output + if (i >= (int)boxes.size() - Output::OUTPUT_SIZE) { + int outidx = i - ( (int)boxes.size() - Output::OUTPUT_SIZE ); + + //jump has different response because we've made it into a change sensitive output + if (outidx == Output::JUMP) out[outidx] = cap(10 * (box->out - box->oldout)); + else out[outidx] = box->out; + } + } +} + +float CPBrain::getActivityRatio() const +{ + float sum = 0; + for (int i = 0; i < (int)boxes.size(); i++){ + sum += fabs(boxes[i].out - boxes[i].oldout); + } + return sum / boxes.size(); +} + +//for mutations which may occur at conception +void CPBrain::initMutate(float MR, float MR2) +{ + //connection (conn) mutations. + //Extraordinary (/100+): new conn, boxify, random type, random source ID, random target ID, + //Rare (/10+): target ID bump, source ID bump, mirror conn, split conn. + //Common: random weight, weight wither, weight jiggle + for(int i= 0; i[B], box [*C] and conn *b is created but without effecting the expected sum on [B]: [A]-a->[*C]-*b->[B] + //locate a dead box + int linkid = -1; + for (int j=0; j < (int)boxes.size(); j++){ + if (boxes[j].dead) { + linkid = j; + if (randf(0,1) < 0.25) break; //add a little randomness to the selection process + } + } + if (linkid>=0) { + int targetid = conns[i].tid; //back up the target box [B] + conns[i].tid = linkid; + + //set box values to mock values to allow pass-through. Note: the pass-through will always be imperfect, so this mutation may be more often negative + boxes[linkid].bias = 0; + boxes[linkid].gw = 1; + boxes[linkid].kp = 1; + boxes[linkid].out = 0.5; + boxes[linkid].oldout = 0.5; + boxes[linkid].target = 0.5; + + CPConn a = CPConn((int)boxes.size()); //create new conn + a.sid = linkid; + a.tid = targetid; + a.w = 1.0; + a.type = 0; + conns.push_back(a); + boxes[boxRef(targetid)].seed = 0; + } + } + + if (randf(0,1) < MR/200) { + //randomize type + conns[i].type = randi(0,2); //remember randi is [a,b). Current options are: 0,1 + conns[i].seed = 0; + boxes[boxRef(conns[i].tid)].seed = 0; + } + + if (randf(0,1) < MR/100) { + //randomize source ID + conns[i].sid = capm(randi(0,(int)boxes.size()), -Input::INPUT_SIZE, boxes.size()-1); + conns[i].seed = 0; + boxes[boxRef(conns[i].tid)].seed = 0; + } + + if (randf(0,1) < MR/100) { + //randomize target ID + boxes[boxRef(conns[i].tid)].seed = 0; //reset the tid's box before leaving it + conns[i].tid = capm(randi(0,(int)boxes.size()), 0, (int)boxes.size()-1); + conns[i].seed = 0; + boxes[boxRef(conns[i].tid)].seed = 0; + } + + //Rare: + if (randf(0,1) < MR/50) { + //target ID bump: +/- 1 + int oldid = conns[i].tid; + int range = (int)(MR2*100); + int newid = capm(oldid + randi(-range,range+1), 0, (int)boxes.size()-1); + //+ (int)(MR2*100*randi(-1,2)) this is a jump mutation; implement later + conns[i].tid = newid; + if (oldid != newid) { + conns[i].seed = 0; + boxes[boxRef(oldid)].seed = 0; + boxes[boxRef(newid)].seed = 0; + } + } + + if (randf(0,1) < MR/40) { + //source ID bump: +/- 1 + int oldid = conns[i].sid; + int range = (int)(MR2*100); + int newid = capm(oldid + randi(-range,range+1), -Input::INPUT_SIZE, boxes.size()-1); + //+ (int)(MR2*100*randi(-1,2)) + conns[i].sid = newid; + if (oldid != newid) { + conns[i].seed = 0; + boxes[boxRef(conns[i].tid)].seed = 0; + } + } + + if (randf(0,1) < MR/30) { + //mirror conn: conn gets -w + conns[i].w = -conns[i].w; + } + + if (randf(0,1) < MR/20) { + //split conn: new conn created from old conn, both get weight / 2 + conns[i].w /= 2; + conns[i].seed = 0; + CPConn copy = conns[i]; + conns.push_back(copy); + } + + //Common: + if (randf(0,1) < MR/10) { + //randomize weight + CPConn dummy = CPConn(conf::BRAINBOXES); + conns[i].w = dummy.w; + conns[i].seed = 0; + boxes[boxRef(conns[i].tid)].seed = 0; + } + + if (randf(0,1) < MR/2) { + //weight wither + if(randf(0,1) > fabs(conns[i].w)*0.2) { + //the closer to 0 it already is, the higher the chance it gets set to 0. *0.2 rescales it from range (-5,5) + conns[i].w = 0; + conns[i].seed = 0; + boxes[boxRef(conns[i].tid)].seed = 0; + } + } + + if (randf(0,1) < MR) { + //weight jiggle + conns[i].w += randn(0, MR2/2); + } + } + + //box mutations + //Extraordinary (/100+): copy box, random bias, random global weight + //Rare (/10+): mirror global weight, mirror bias + //Common: random kp, kp jiggle, global weight jiggle, bias jiggle + for (int i=0; i < (int)boxes.size(); i++){ + CPBox* box= &boxes[i]; + + //Extraordinary: + if (randf(0,1)bias = boxes[j].bias; + box->kp = boxes[j].kp; + box->gw = boxes[j].gw; + box->seed = 0; + } + } + + if (randf(0,1)bias = dummy.bias; + box->seed = 0; + } + + if (randf(0,1)gw = dummy.gw; + box->seed = 0; + } + + //Rare: + if (randf(0,1)gw = -box->gw; + box->seed = 0; + } + + if (randf(0,1)bias = -box->bias; + box->seed = 0; + } + + //Common: + if (randf(0,1)kp = dummy.kp; + box->seed = 0; + } + + if (randf(0,1)gw += randn(0, MR2/8); + } + + if (randf(0,1)kp = cap(randn(box->kp, MR2/5)); + } + + if (randf(0,1)bias += randn(0, MR2); + } + } + + //clean up and reset brain conns and live boxes + resetBrain(); +} + +void CPBrain::liveMutate(float MR, float MR2, vector& out) +{ + //for mutations which may occur while the bot is live + //live mutations are applied to the boxes and conns as normal, but only jiggle changes are allowed + int randconn = randi(0, conns.size()); + + if (conf::LEARNRATE>0 && conns[randconn].sid >= 0 && randf(0,1) 0.5) { + //modify weights based on matching old output and old input, if stimulant is active + float sourceoldout = boxes[boxRef(conns[randconn].sid)].oldout; + float targetoldout = boxes[boxRef(conns[randconn].tid)].oldout; + conns[randconn].w += conf::LEARNRATE * (2*stim - 1) * (1 - abs(targetoldout - sourceoldout)); + } + } + + if (randf(0,1) < MR/5) { + //weight wither + if(randf(0,1) > fabs(conns[randconn].w)) {//the closer to 0 it already is, the higher the chance it gets set to 0 + conns[randconn].w = 0; + conns[randconn].seed = 0; + boxes[boxRef(conns[randconn].tid)].seed = 0; + } + } + + if (randf(0,1) < MR) { + //weight jiggle + conns[randconn].w += randn(0, MR2); + //don't bother with seed = 0, this is too common and too low impact + } + + //box mutation + CPBox* box= &boxes[randi(0, boxes.size())]; + + if (randf(0,1)gw += randn(0, MR2/5); + } + + if (randf(0,1)kp = cap(randn(box->kp, MR2/10)); + } + + if (randf(0,1)bias += randn(0, MR2); + //no need to reset seed for simple bias jiggle + } +} + +CPBrain CPBrain::crossover(const CPBrain& other) +{ + CPBrain newbrain = *this; + if (this->conns.size() < other.conns.size()) { + newbrain = other; + } + + for (int i=0; i < (int)newbrain.conns.size(); i++){ + if(i >= other.conns.size() || i >= this->conns.size()) continue; //allow the rest of the conns to be whichever they were inherited from + + int s1= this->conns[i].seed; + int s2= other.conns[i].seed; + //function which offers probability of which parent to use, based on relative seed counters + float threshold= ((s1 - s2) / (conf::BRAINSEEDHALFTOLERANCE + abs(s1 - s2)) + 1) / 2; + + if(randf(0,1)conns[i]; + } else { + newbrain.conns[i] = other.conns[i]; + } + newbrain.conns[i].seed++; + } + + for (int i = 0; i < (int)newbrain.boxes.size(); i++){ + if(i >= other.boxes.size() || i >= this->boxes.size()) continue; //allowing for variable box counts (NOT IMPLEMENTED) + + int s1= this->boxes[i].seed; + int s2= other.boxes[i].seed; + //function which offers probability of which parent to use, based on relative seed counters + float threshold= ((s1 - s2) / (conf::BRAINSEEDHALFTOLERANCE + abs(s1 - s2)) + 1) / 2; + + if(randf(0,1)boxes[i]; + } else { + newbrain.boxes[i] = other.boxes[i]; + } + newbrain.boxes[i].seed++; + } + return newbrain; +} + +/*std::vector CPBrain::traceBack(int outback) +{ + std::vector inputs; //list of boxes which effect our Output of Interest + std::vector todos; //list of boxes still need to check + std::vector diddos; //list of boxes done. Continually sorted + + //if bad data, break + if(outback<0 || outback>=Output::OUTPUT_SIZE) { + printf("bad trace value passed\n"); + return inputs; + } + + int nowtrace= boxes.size()-1-outback; + int tries= 0; + //give us a natural stopping point with # tries + while(tries<100) { + tries++; + + //for every connection of the current tracing box, + for (int k=0;kconf::BRAIN_TRACESTRENGTH) { + + //next, see if it references an input. + if(boxes[nowtrace].id[k]0) printf("we had %d nodes left to try before giving up\n", todos.size()); + + return inputs; +} NOT IMPLEMENTED*/ + diff --git a/SOURCE/CPBrain.h b/SOURCE/CPBrain.h new file mode 100644 index 0000000..cf9586e --- /dev/null +++ b/SOURCE/CPBrain.h @@ -0,0 +1,80 @@ +#ifndef CPBRAIN_H +#define CPBRAIN_H + +#include "settings.h" +#include "helpers.h" + +#include +#include +#include + +class CPBox { +public: + CPBox(); + CPBox(int numboxes); + + float kp; //damper, always in range [0,1] + float gw; //global weight + float bias; //base number + + bool dead; //t/f flag for if this box has downstream connections. If not, we don't visualize; it's not important. Output boxes are never dead + int seed; //number of successes (reproduction events) this box has experienced whilst unmodified + + //state variables + float acc; //accumulated value. This is used b/c we simulate conns instead of boxes, so we need to track each box's total before gweight and sigmoid + float target; //target value this node is going toward + float out; //current output + float oldout; //output a tick ago +}; + +class CPConn { +public: + CPConn(); + CPConn(int boxes); + + float w; //weight of each connecting box + int sid; //source. +: box id in boxes[], -: input id in inputs list + int tid; //target box id in boxes[] + int type; //0: regular synapse. 1: change-sensitive synapse. 2: NOT IMPLEMENTED memory trigger synapse + + bool dead; //t/f flag for if this conn links to a dead target or not. Can also become dead if weight == 0 + int seed; //number of successes (reproduction events) this conn has experienced whilst unmodified +}; + +/** + * Connection-Positive brain + */ +class CPBrain +{ +public: + + std::vector boxes; //list of boxes + std::vector conns; //list of simulated connections. No limits on target conns, source conns, or even number of conns + + CPBrain(); + CPBrain(int numboxes, int numconns); + virtual CPBrain& operator=(const CPBrain& other); + void setLives(); + void resetLives(); + void resetBrain(); + void healthCheck(); + + int inRef(int id); + int boxRef(int id); + int connRef(int id); + + void tick(std::vector& in, std::vector& out); + + float getActivityRatio() const; +// std::vector traceBack(int outback); + + void initMutate(float MR, float MR2); + void liveMutate(float MR, float MR2, std::vector& out); + CPBrain crossover( const CPBrain &other ); + + +private: + void init(); +}; + +#endif diff --git a/SOURCE/GLView.cpp b/SOURCE/GLView.cpp index 1c13a9e..4865fd3 100644 --- a/SOURCE/GLView.cpp +++ b/SOURCE/GLView.cpp @@ -15,6 +15,7 @@ #include #include +#include //OpenGL callbacks void gl_processNormalKeys(unsigned char key, int x, int y) @@ -145,8 +146,7 @@ GLView::GLView(World *s) : lastUpdate(0), mousedrag(false), uiclicked(false), - ui_layerpreset(0), - ui_sadmode(1) + ui_layerpreset(0) { xtranslate= 0.0; @@ -159,6 +159,8 @@ GLView::GLView(World *s) : savehelper= new ReadWrite(); currentTime= glutGet(GLUT_ELAPSED_TIME); lastsavedtime= currentTime; + + ui_ladmode = LADVisualMode::GHOST; } @@ -172,17 +174,17 @@ void GLView::gotoDefaultZoom() //Control.cpp { //reset width height just this once - wWidth= glutGet(GLUT_WINDOW_WIDTH); - wHeight= glutGet(GLUT_WINDOW_HEIGHT); + wWidth = glutGet(GLUT_WINDOW_WIDTH); + wHeight = glutGet(GLUT_WINDOW_HEIGHT); //do some mathy things to zoom and translate correctly - float scaleA= (float)wWidth/(conf::WIDTH+2200); - float scaleB= (float)wHeight/(conf::HEIGHT+150); - if(scaleA>scaleB) scalemult= scaleB; - else scalemult= scaleA; - xtranslate= -(conf::WIDTH-2020)/2; - ytranslate= -(conf::HEIGHT)/2; - live_follow= 0; + float scaleA = (float)wWidth / (conf::WIDTH + 2200); + float scaleB = (float)wHeight / (conf::HEIGHT + 150); + if(scaleA > scaleB) scalemult = scaleB; + else scalemult = scaleA; + xtranslate = -(conf::WIDTH - 3200)/2; + ytranslate = -(conf::HEIGHT)/2; + live_follow = 0; } @@ -226,8 +228,8 @@ void GLView::changeSize(int w, int h) //update UI elements and make sure they are still in view for(int i=0; iprocessMouse(button, state, wx, wy, scalemult) && live_selection!=Select::RELATIVE) live_selection = Select::MANUAL; - } else if(live_cursormode==1){ //placement mode + } else if(live_cursormode==MouseMode::PLACE_AGENT){ if(world->addLoadedAgent(wx, wy)) printf("agent placed! yay\n"); else world->addEvent("No agent loaded! Load an Agent 1st", EventColor::BLOOD); } @@ -283,7 +285,7 @@ void GLView::processMouseActiveMotion(int x, int y) if (downb[1]==1) { //mouse wheel. Change scale - scalemult -= conf::ZOOM_SPEED*(y-mousey); + scalemult += conf::ZOOM_SPEED*scalemult*((mousey-y) - (mousex-x)); if(scalemult<0.03) scalemult=0.03; } @@ -316,7 +318,7 @@ void GLView::processMousePassiveMotion(int x, int y) void GLView::handlePopup(int x, int y) { int layers= getLayerDisplayCount(); - if(layers>0 && layers0 && layers < DisplayLayer::DISPLAYS) { //only show popup if less than //convert mouse x,y to world x,y int worldcx= (int)((((float)x-wWidth*0.5)/scalemult-xtranslate)); int worldcy= (int)((((float)y-wHeight*0.5)/scalemult-ytranslate)); @@ -327,11 +329,11 @@ void GLView::handlePopup(int x, int y) worldcx/= conf::CZ; worldcy/= conf::CZ; //convert to cell coords - if(world->isDebug()) printf("worldcx: %d, worldcy %d\n", worldcx, worldcy); popupReset(x+12, y); //clear and set popup position near mouse sprintf(line, "Cell x: %d, y: %d", worldcx, worldcy); popupAddLine(line); + if(world->isDebug()) printf("worldcx: %d, worldcy %d\n", worldcx, worldcy); if(live_layersvis[DisplayLayer::ELEVATION]) { float landtype= ceilf(world->cells[layer][worldcx][worldcy]*10)*0.1; @@ -484,6 +486,7 @@ void GLView::processReleasedKeys(unsigned char key, int x, int y) world->addEvent(" '~' mutates selected agent", EventColor::CYAN); } else if (key==9) { //[tab] - tab toggles to manual selection mode live_selection= Select::MANUAL; + world->pcontrol= false; }//else if (key=='g') { //graphics details //MAKE SURE ALL GRAPHICS CHANGES CATELOGUED HERE //world->addEvent("",9); @@ -508,7 +511,7 @@ void GLView::menu(int key) if(scalemult<0.03) scalemult=0.03; } else if (key==9) { //[tab] - press sets select mode to off, and sets cursor mode to default live_selection= Select::NONE; - live_cursormode= 0; + live_cursormode= MouseMode::SELECT; } else if (key=='c') { world->setClosed( !world->isClosed() ); live_worldclosed= (int) world->isClosed(); @@ -518,8 +521,7 @@ void GLView::menu(int key) // world->selectedTrace(1); } else if (key=='f') { live_follow= !live_follow; //toggle follow selected agent - } else if (key=='m') { //drawing - world->setDemo(false); + } else if (key=='m') { //drawing / fast mode live_fastmode= !live_fastmode; } else if (key=='n') { //dismiss visible world events world->dismissNextEvents(conf::EVENTS_DISP); @@ -540,6 +542,8 @@ void GLView::menu(int key) } else if(key=='q') { //zoom and translocate to instantly see the whole world gotoDefaultZoom(); + } else if (key=='j') { //toggle hide dead + live_hidedead = 1-live_hidedead; } else if (key=='z' || key=='x') { //change agent visual scheme; x= "next", z= "previous" if (key=='x') live_agentsvis++; else live_agentsvis--; @@ -575,13 +579,14 @@ void GLView::menu(int key) if(live_selection!=key-47) live_selection= key-47; else live_selection= Select::NONE; } else if(key==48) { //number key 0: select random from alive - while(true){ //select random agent, among alive + int count = 0; + while(count<10000){ //select random agent, among alive int idx= randi(0,world->agents.size()); if (world->agents[idx].health>0.1) { world->setSelectedAgent(idx); live_selection= Select::MANUAL; break; - } + } else count++; } //user controls: @@ -663,6 +668,7 @@ void GLView::menu(int key) world->reset(); world->spawn(); printf("WORLD RESET!\n"); + world->addEvent("World Started!", EventColor::MINT); } else if (key==1008) { //save world handleRW(RWOpen::BASICSAVE); } else if (key==1009) { //load world @@ -670,6 +676,7 @@ void GLView::menu(int key) } else if (key==1010) { //reload config world->readConfig(); //.cfg/.sav Update live variables + live_demomode= (int)(world->isDemo()); live_worldclosed= (int)(world->isClosed()); live_landspawns= (int)(world->DISABLE_LAND_SPAWN); live_moonlight= (int)(world->MOONLIT); @@ -683,7 +690,9 @@ void GLView::menu(int key) } else if (key==1013) { //toggle demo mode world->setDemo(!world->isDemo()); } else { + #if defined(_DEBUG) printf("Unmatched key pressed: %i\n", key); + #endif } } @@ -692,12 +701,16 @@ void GLView::menuSpecial(int key) // special control keys { if (key==GLUT_KEY_UP) { ytranslate+= 20/scalemult; + live_follow= 0; } else if (key==GLUT_KEY_LEFT) { xtranslate+= 20/scalemult; + live_follow= 0; } else if (key==GLUT_KEY_DOWN) { ytranslate-= 20/scalemult; + live_follow= 0; } else if (key==GLUT_KEY_RIGHT) { xtranslate-= 20/scalemult; + live_follow= 0; } else if (key==GLUT_KEY_PAGE_DOWN) { if(world->setSelectionRelative(-1)) live_selection= Select::MANUAL; } else if (key==GLUT_KEY_PAGE_UP) { @@ -761,6 +774,7 @@ void GLView::gluiCreateMenu() live_worldclosed= 0; live_paused= 0; live_playmusic= 1; + live_playsounds= 1; live_fastmode= 0; live_skipdraw= 1; live_agentsvis= Visual::RGB; @@ -773,6 +787,8 @@ void GLView::gluiCreateMenu() live_debug= world->isDebug(); live_grid= 0; live_hidedead= 0; + live_hidegenz= 0; + live_demomode= (int)world->isDemo(); live_landspawns= (int)world->DISABLE_LAND_SPAWN; live_moonlight= (int)world->MOONLIT; live_oceanpercent= world->OCEANPERCENT; @@ -782,7 +798,7 @@ void GLView::gluiCreateMenu() live_climate= (int)world->CLIMATE; live_climatebias= world->CLIMATEBIAS; live_climatemult= world->CLIMATEMULT; - live_cursormode= 0; + live_cursormode= MouseMode::SELECT; char text[32]= ""; #if defined(_DEBUG) //disable some extra features in debug environment @@ -882,17 +898,22 @@ void GLView::gluiCreateMenu() else if(i==Visual::HEALTH) strcpy(text, "Health"); else if(i==Visual::REPCOUNTER) strcpy(text, "Rep. Counter"); else if(i==Visual::METABOLISM) strcpy(text, "Metabolism"); + else if(i==Visual::STRENGTH) strcpy(text, "Strength"); + else if(i==Visual::BRAINMUTATION) strcpy(text, "Brain Mutability"); + else if(i==Visual::GENEMUTATION) strcpy(text, "Gene Mutability"); else if(i==Visual::LUNGS) strcpy(text, "Lungs"); + else if(i==Visual::GENERATIONS) strcpy(text, "Generation"); // else if(i==Visual::REPMODE) strcpy(text, "Rep. Mode"); else strcpy(text, "UNKNOWN_Visual"); new GLUI_RadioButton(group_agents,text); } Menu->add_checkbox_to_panel(rollout_vis, "Hide Dead", &live_hidedead); + Menu->add_checkbox_to_panel(rollout_vis, "Hide Gen 0", &live_hidegenz); Menu->add_checkbox_to_panel(rollout_vis, "Grid on", &live_grid); Menu->add_checkbox_to_panel(rollout_vis, "Water FX", &live_waterfx); - GLUI_Rollout *rollout_xyl= new GLUI_Rollout(Menu,"Selection Mode"); + GLUI_Rollout *rollout_xyl= new GLUI_Rollout(Menu,"Selection Mode",false); GLUI_RadioGroup *group_select= new GLUI_RadioGroup(rollout_xyl,&live_selection); for(int i=0; iadd_button_to_panel(rollout_xyl, "Save Selected", RWOpen::BASICSAVEAGENT, glui_handleRW); LoadAgentButton= Menu->add_button_to_panel(rollout_xyl, "Load Agent", RWOpen::BASICLOADAGENT, glui_handleRW); - Menu->add_checkbox("Play Music?",&live_playmusic); + Menu->add_checkbox("Play Music",&live_playmusic); + Menu->add_checkbox("Play Sounds",&live_playsounds); + Menu->add_checkbox("Demo Mode",&live_demomode); Menu->add_checkbox("DEBUG",&live_debug); //set to main graphics window @@ -928,13 +951,15 @@ void GLView::gluiCreateMenu() void GLView::handleButtons(int action) //Control.cpp (only if not immediately switching UI systems) { - if (action==GUIButtons::TOGGLE_LAYERS) { - if(ui_layerpreset!=0) ui_layerpreset= 0; - else ui_layerpreset= DisplayLayer::DISPLAYS*2; + if (action == GUIButtons::TOGGLE_LAYERS) { + if(ui_layerpreset != 0) ui_layerpreset = 0; + else ui_layerpreset = DisplayLayer::DISPLAYS*2; processLayerPreset(); //call to method to update layer bools based on ui_layerpreset - } else if (action==GUIButtons::TOGGLE_PLACEAGENTS) { - live_cursormode= 1-live_cursormode; + + } else if (action == GUIButtons::TOGGLE_PLACEAGENTS) { + if(live_cursormode != MouseMode::PLACE_AGENT) live_cursormode = MouseMode::PLACE_AGENT; + else live_cursormode = MouseMode::SELECT; } } @@ -946,7 +971,7 @@ void GLView::handleRW(int action) //glui callback for saving/loading worlds { live_paused= 1; //pause world while we work - if (action==RWOpen::STARTLOAD){ //basic load option selected + if (action == RWOpen::STARTLOAD){ //basic load option selected //Step 1 of loading: check if user really wants to reset world if(!world->isDemo()){ //no need for alerts if in demo mode (epoch == 0) //get time since last save @@ -979,12 +1004,12 @@ void GLView::handleRW(int action) //glui callback for saving/loading worlds } } else handleRW(RWOpen::BASICLOAD); - } else if (action==RWOpen::IGNOREOVERLOAD) { + } else if (action == RWOpen::IGNOREOVERLOAD) { //alert window open before load, was ignored by user. Close it first before loading Alert->hide(); handleRW(RWOpen::BASICLOAD); - } else if (action==RWOpen::BASICLOAD) { //load option promt + } else if (action == RWOpen::BASICLOAD) { //load option promt Loader = GLUI_Master.create_glui("Load World",0,50,50); new GLUI_StaticText(Loader,""); @@ -996,7 +1021,7 @@ void GLView::handleRW(int action) //glui callback for saving/loading worlds Loader->set_main_gfx_window(win1); - } else if (action==RWOpen::BASICSAVE) { //save option prompt + } else if (action == RWOpen::BASICSAVE) { //save option prompt Saver = GLUI_Master.create_glui("Save World",0,50,50); new GLUI_StaticText(Saver,""); @@ -1008,7 +1033,7 @@ void GLView::handleRW(int action) //glui callback for saving/loading worlds Saver->set_main_gfx_window(win1); - } else if (action==RWOpen::NEWWORLD){ //new world alert prompt + } else if (action == RWOpen::NEWWORLD){ //new world alert prompt Alert = GLUI_Master.create_glui("Alert",0,50,50); Alert->show(); new GLUI_StaticText(Alert,""); @@ -1021,22 +1046,22 @@ void GLView::handleRW(int action) //glui callback for saving/loading worlds Alert->set_main_gfx_window(win1); - } else if (action==RWOpen::BASICSAVEAGENT) { //save selected agent prompt + } else if (action == RWOpen::BASICSAVEAGENT) { //save selected agent prompt Saver = GLUI_Master.create_glui("Save Agent",0,50,50); new GLUI_StaticText(Saver,""); - Filename= new GLUI_EditText(Saver,"Enter Agent File Name (e.g, 'AGENT'):"); + Filename = new GLUI_EditText(Saver,"Enter Agent File Name (e.g, 'AGENT'):"); new GLUI_StaticText(Saver,""); Filename->set_w(300); new GLUI_Button(Saver,"Save", RWClose::BASICSAVEAGENT, glui_handleCloses); new GLUI_Button(Saver,"Cancel", RWClose::CANCELSAVE, glui_handleCloses); - } else if (action==RWOpen::BASICLOADAGENT) { - if(live_cursormode==0){ + } else if (action == RWOpen::BASICLOADAGENT) { + if(live_cursormode != MouseMode::PLACE_AGENT){ Loader = GLUI_Master.create_glui("Load Agent",0,50,50); new GLUI_StaticText(Loader,""); - Filename= new GLUI_EditText(Loader,"Enter Agent File Name (e.g, 'AGENT'):"); + Filename = new GLUI_EditText(Loader,"Enter Agent File Name (e.g, 'AGENT'):"); new GLUI_StaticText(Loader,""); Filename->set_w(300); new GLUI_Button(Loader,"Load", RWClose::BASICLOADAGENT, glui_handleCloses); @@ -1044,8 +1069,8 @@ void GLView::handleRW(int action) //glui callback for saving/loading worlds Loader->set_main_gfx_window(win1); } else { - live_cursormode= 0; //return cursor mode to normal if we press button again - live_paused= 0; + live_cursormode = MouseMode::SELECT; //return cursor mode to normal if we press button while in placement mode + live_paused = 0; LoadAgentButton->set_name("Load Agent"); } @@ -1110,7 +1135,7 @@ void GLView::handleCloses(int action) //GLUI callback for handling window closin } else { fclose(fl); //we are technically going to open the file again in savehelper... oh well - savehelper->loadWorld(world, xtranslate, ytranslate, filename); + savehelper->loadWorld(world, xtranslate, ytranslate, scalemult, filename); lastsavedtime= currentTime; //reset last saved time; we have nothing before the load to save! @@ -1129,6 +1154,7 @@ void GLView::handleCloses(int action) //GLUI callback for handling window closin world->spawn(); lastsavedtime= currentTime; printf("WORLD RESET!\n"); + world->addEvent("World Started!", EventColor::MINT); //this is getting annoying - so if the world changes any values that have a GUI, the GUI will not update. We have to force update here syncLiveWithWorld(); Alert->hide(); @@ -1162,20 +1188,20 @@ void GLView::handleCloses(int action) //GLUI callback for handling window closin // } else { // fclose(ck); FILE* sa = fopen(address.c_str(), "w"); - int sidx= world->getSelectedAgent(); + int sidx= world->getSelectedAgentIndex(); if (sidx>=0){ Agent *a= &world->agents[sidx]; savehelper->saveAgent(a, sa); std::string selectedsaved= "Agent saved as \""; selectedsaved.append(filename); selectedsaved.append("\""); - world->addEvent(selectedsaved, EventColor::CYAN); + world->addEvent(selectedsaved, EventColor::WHITE); } fclose(sa); // } } Saver->hide(); - } else if (action==RWClose::BASICLOADAGENT){ //basic agent loader + } else if (action == RWClose::BASICLOADAGENT){ //basic agent loader strcpy(filename, Filename->get_text()); if (checkFileName(filename)) { @@ -1205,7 +1231,7 @@ void GLView::handleCloses(int action) //GLUI callback for handling window closin savehelper->loadAgentFile(world, address.c_str()); - live_cursormode= 1; //activate agent placement mode + live_cursormode = MouseMode::PLACE_AGENT; //activate agent placement mode LoadAgentButton->set_name("UNLOAD Agent"); } } @@ -1217,7 +1243,7 @@ void GLView::handleCloses(int action) //GLUI callback for handling window closin strcpy(filename,file_name.c_str()); if (checkFileName(filename)) { - savehelper->loadWorld(world, xtranslate, ytranslate, filename); + savehelper->loadWorld(world, xtranslate, ytranslate, scalemult, filename); } Loader->hide(); //NEEDS LOTS OF WORK @@ -1330,15 +1356,15 @@ void GLView::trySaveWorld(bool force, bool autosave) fclose(ck); } else { if(ck) fclose(ck); - savehelper->saveWorld(world, xtranslate, ytranslate, filename); + savehelper->saveWorld(world, xtranslate, ytranslate, scalemult, filename); lastsavedtime= currentTime; if(!autosave){ if (!force) { std::string selectedsaved= "World saved as \""; selectedsaved.append(filename); selectedsaved.append("\""); - world->addEvent(selectedsaved, EventColor::CYAN); - } else world->addEvent("Saved World (overwritten)", EventColor::CYAN); + world->addEvent(selectedsaved, EventColor::WHITE); + } else world->addEvent("Saved World (overwritten)", EventColor::WHITE); } } } @@ -1407,15 +1433,19 @@ void GLView::checkTileListClicked(std::vector tiles, int mx, int my, uiclicked= true; //this must be exposed to mouse state==0 so that it catches drags off of UI elements - if(tiles[ti].clickable && state==1){ //if clickable, process actions - if(tiles[ti].key=="Follow") { - live_follow= 1-live_follow; - ui_sadmode= 1; + if(tiles[ti].clickable && state == 1){ //if clickable, process actions + if(tiles[ti].key == "Follow") { + live_follow = 1-live_follow; + ui_ladmode = LADVisualMode::GHOST; + } + else if(tiles[ti].key == "Damages") { + if (ui_ladmode != LADVisualMode::DAMAGES) ui_ladmode = LADVisualMode::DAMAGES; + else ui_ladmode = LADVisualMode::GHOST; + } + else if(tiles[ti].key=="Intake") { + if (ui_ladmode != LADVisualMode::INTAKES) ui_ladmode = LADVisualMode::INTAKES; + else ui_ladmode = LADVisualMode::GHOST; } - else if(tiles[ti].key=="Damages" && ui_sadmode!=2) ui_sadmode= 2; - else if(tiles[ti].key=="Damages" && ui_sadmode==2) ui_sadmode= 1; - else if(tiles[ti].key=="Intake" && ui_sadmode!=3) ui_sadmode= 3; - else if(tiles[ti].key=="Intake" && ui_sadmode==3) ui_sadmode= 1; } //finally, check our tile's sub-tiles list, and all their sub-tiles, etc. @@ -1429,20 +1459,22 @@ void GLView::processTiles() //Control.cpp { for(int ti= 0; tigetSelectedAgent()<0 && live_cursormode!=1) { maintiles[ti].hide(); continue; } - else maintiles[ti].show(); //hide/unhide as needed + if(ti==MainTiles::LAD){ + //LAD: Loaded/seLected Agent Display + if(world->getSelectedAgentIndex()<0 && live_cursormode != MouseMode::PLACE_AGENT) { + maintiles[ti].hide(); continue; + } else maintiles[ti].show(); for(int tichild= 0; tichildgetSelectedAgent()<0) { + /*if(world->getSelectedAgentIndex()<0) { if(maintiles[ti].y>-maintiles[ti].h) maintiles[ti].y--; //tuck it away when we lost a selected agent else { maintiles[ti].hide(); continue; } //hide and skip display completely if hidden @@ -1486,6 +1518,7 @@ int GLView::getAgentRes(bool ghost){ void GLView::syncLiveWithWorld() { //.cfg/.sav make sure to put all saved world variables with GUI options here so they update properly! + live_demomode= (int)world->isDemo(); live_worldclosed= (int)world->isClosed(); live_landspawns= (int)world->DISABLE_LAND_SPAWN; live_moonlight= (int)world->MOONLIT; @@ -1511,9 +1544,14 @@ void GLView::handleIdle() GLUI_Master.sync_live_all(); //after syncing all the live vars with GLUI_Master, set the vars they represent to their proper values. + world->setDemo(live_demomode); world->setClosed(live_worldclosed); world->DISABLE_LAND_SPAWN= (bool)live_landspawns; - world->MOONLIT= (bool)live_moonlight; + if((int)world->MOONLIT!=live_moonlight) { + if(live_moonlight) world->addEvent("Moon formation event!", EventColor::BLACK); + else world->addEvent("Moon, uh... explosion event?!", EventColor::BLACK); + world->MOONLIT = (bool)live_moonlight; + } world->OCEANPERCENT= live_oceanpercent; world->DROUGHTS= (bool)live_droughts; world->DROUGHTMULT= live_droughtmult; @@ -1522,6 +1560,7 @@ void GLView::handleIdle() world->CLIMATEBIAS= live_climatebias; world->CLIMATEMULT= live_climatemult; world->domusic= (bool)live_playmusic; + if (!live_fastmode) world->dosounds= (bool)live_playsounds; world->setDebug((bool) live_debug); #if defined(_DEBUG) @@ -1536,16 +1575,16 @@ void GLView::handleIdle() //create the UI tiles //I don't like destroying and creating these live, but for reasons beyond me, initialization won't accept variables like glutGet() - int sadheight= 3*UID::BUFFER + ((int)ceil((float)SADHudOverview::HUDS*0.333333))*(UID::CHARHEIGHT + UID::BUFFER); - //The SAD requires a special height measurement based on the Hud construct, expanding it easily if more readouts are added + int ladheight= 3*UID::BUFFER + ((int)ceil((float)LADHudOverview::HUDS*0.333333))*(UID::CHARHEIGHT + UID::BUFFER); + //The LAD requires a special height measurement based on the Hud construct, expanding it easily if more readouts are added maintiles.clear(); for(int i=0; iNO_TIPS || world->isDebug()){ //and not if world says NO or is in debug mode - if(world->modcounter%conf::TIPS_PERIOD==conf::TIPS_DELAY) { - if(world->getDay()<7) { - //the first 7 game days show the first few tips from the array of tips - int rand_tip_end= randi(0,world->tips.size()); - int min_tip= (int)(world->modcounter/conf::TIPS_PERIOD)modcounter/conf::TIPS_PERIOD) : rand_tip_end; - world->addEvent(world->tips[min_tip].c_str(), EventColor::YELLOW); - } - else world->addEvent(world->tips[randi(0,world->tips.size())].c_str(), EventColor::YELLOW); + if(world->events.size() < conf::EVENTS_DISP && world->modcounter % conf::TIPS_PERIOD == conf::TIPS_DELAY) { + //don't add tips if we got a lot of events showing + int rand_tip = randi(0,world->tips.size()); + int tip_for_modcounter = (int)((float)world->modcounter/conf::TIPS_PERIOD); + + if(world->getDay() <= 2) { + //based on the math of 2 day * 8000 ticks / day * 1 tip / TIPS_PERIOD ticks, we get 20 tips in linear fashion + int selected_tip = tip_for_modcounter < world->tips.size() ? tip_for_modcounter : rand_tip; + world->addEvent( world->tips[selected_tip].c_str(), EventColor::YELLOW ); + } else if (world->getDay() <= 7) { + //the next 5 game days show the first few tips from the array of tips, but with some randomness possible + int selected_tip = tip_for_modcounter < rand_tip ? tip_for_modcounter : rand_tip; + world->addEvent( world->tips[selected_tip].c_str(), EventColor::YELLOW ); + } else if (world->modcounter % (conf::TIPS_PERIOD*2) == conf::TIPS_DELAY) { + //the rest get displayed more rarely, until tips get turned off + world->addEvent( world->tips[rand_tip].c_str(), EventColor::YELLOW ); + } } - } else if (world->modcounter==conf::TIPS_DELAY) world->addEvent("To re-enable tips, see settings.cfg", EventColor::YELLOW); + } else if (world->modcounter == conf::TIPS_DELAY) { + //provide a tip about re-enabling tips + world->addEvent("To re-enable tips, see settings.cfg", EventColor::YELLOW); + } } } @@ -1579,12 +1630,12 @@ void GLView::handleIdle() syncLiveWithWorld(); //autosave world periodically, based on world time - if (live_autosave==1 && world->modcounter%(world->FRAMES_PER_DAY*10)==0){ + if (live_autosave==1 && !world->isDemo() && world->modcounter%(world->FRAMES_PER_DAY*10)==0){ trySaveWorld(true,true); } //Do some recordkeeping and let's intelligently prevent users from simulating nothing - if(world->getAgents()<=0 && live_worldclosed==1) { + if(live_worldclosed==1 && world->getAgents()<=0) { live_worldclosed= 0; live_autosave= 0; //I'm gonna guess you don't want to save the world anymore world->addEvent("Disabled Closed world, nobody was home!", EventColor::MINT); @@ -1594,16 +1645,15 @@ void GLView::handleIdle() currentTime = glutGet(GLUT_ELAPSED_TIME); frames++; if ((currentTime - lastUpdate) >= 1000) { - char fastmode; + char ratemode; if(live_fastmode){ - fastmode= 'T'; + ratemode= 'T'; if(live_paused) frames= 0; //if we're paused and in fast mode, we're not exactly rendering any frames //we technically are rendering if just paused tho, so this check should stay here - } else fastmode='F'; + } else ratemode='F'; - sprintf( buf, "Evagents - %cPS: %d Alive: %d Herbi: %d Carni: %d Frugi: %d Epoch: %d Day %d", - fastmode, frames, world->getAgents()-world->getDead(), world->getHerbivores(), world->getCarnivores(), - world->getFrugivores(), world->getEpoch(), world->getDay() ); + sprintf( buf, "Evagents - %cPS: %d Alive: %d Epoch: %d Day %d", + ratemode, frames, world->getAgents()-world->getDead(), world->getEpoch(), world->getDay() ); glutSetWindowTitle( buf ); world->timenewsong--; //reduce our song delay timer @@ -1612,7 +1662,7 @@ void GLView::handleIdle() } if (!live_fastmode) { - world->dosounds= true; + if(live_playsounds) world->dosounds= true; if (live_skipdraw>0) { if (modcounter%live_skipdraw==0) renderScene(); //increase fps by skipping drawing @@ -1628,7 +1678,6 @@ void GLView::handleIdle() //world->audio->stopAllSounds(); DON'T do this, too jarring to cut off all sounds world->dosounds= false; - world->setDemo(false); //in fast mode, our sim forgets to check nearest relative and reset the selection if(live_selection==Select::RELATIVE) world->setSelection(live_selection); //but we don't want to do this for all selection types because it slows fast mode down @@ -1749,37 +1798,49 @@ Color3f GLView::setColorHealth(float health) Color3f GLView::setColorStomach(const float stomach[Stomach::FOOD_TYPES]) { + float plant= stomach[Stomach::PLANT]; + float fruit= stomach[Stomach::FRUIT]; + float meat= stomach[Stomach::MEAT]; Color3f color; - color.red= cap(stomach[Stomach::MEAT]+stomach[Stomach::FRUIT]-pow(stomach[Stomach::PLANT],2)/2); - color.gre= cap(pow(stomach[Stomach::PLANT],2)/2+stomach[Stomach::FRUIT]-stomach[Stomach::MEAT]/2); - color.blu= stomach[Stomach::MEAT]*stomach[Stomach::PLANT]/2; + color.red= meat + fruit*0.9 - plant/4*(1 + fruit); + color.gre= plant/2 + fruit/3*(3 + 0.5*plant*(meat - 2) - meat); + color.blu= meat*plant*(0.45 + pow(fruit,2)); //these have been *carefully* selected and balanced return color; } Color3f GLView::setColorTempPref(float discomfort) { Color3f color; - color.red= sqrt(cap(8*discomfort)); - color.gre= discomfort==0 ? 1.0 : cap(0.75-2*discomfort); - color.blu= discomfort==0 ? 1.0 : cap((2-3*discomfort)/4); + if (discomfort>0) { + color.red= 2*sqrt(cap(2*discomfort))*(1.3-discomfort); + color.gre= 0.9-2*discomfort; + color.blu= (2-16*discomfort)/4; + } else if (discomfort<0) { + color.red= (-discomfort)*(1+discomfort); + color.gre= 0.8+2*discomfort; + color.blu= 1+discomfort; + } else { + color.gre= 1.0; + } + return color; } Color3f GLView::setColorMetabolism(float metabolism) { Color3f color; - color.red= metabolism*metabolism; - color.gre= 2*metabolism*(1-metabolism); - color.blu= (1-metabolism)*(1-metabolism); + color.red= 2*(1-metabolism); + color.gre= 2*(metabolism-0.1)*(1.9-2*metabolism)*(2-metabolism); + color.blu= 4*(3*metabolism-1)*(1-metabolism); return color; } Color3f GLView::setColorTone(float tone) { Color3f color; - color.red= tone<0.8 ? cap(1.6-3*tone) : cap(-3.5+4*tone); //best put these into an online graphing calculator... - color.gre= tone<0.5 ? cap(5*tone-0.2) : cap(2.925-3*tone); - color.blu= cap(4*tone-1.75); + color.red= tone<0.8 ? 1.6-3*tone : -3.5+4*tone; //best put these into an online graphing calculator... + color.gre= tone<0.5 ? 5*tone-0.2 : 2.925-3*tone; + color.blu= 4*tone-1.75; if(tone>0.45 && tone<0.55) { color.red*= 10*fabs(tone-0.5)+0.5; color.gre*= 5*fabs(tone-0.5)+0.75; @@ -1791,18 +1852,18 @@ Color3f GLView::setColorTone(float tone) Color3f GLView::setColorLungs(float lungs) { Color3f color; - color.red= cap((0.2+lungs)*(0.4-lungs) + 25*(lungs-0.45)*(0.8-lungs)); - color.gre= lungs>0.1 ? cap(2.45*(lungs)*(1.2-lungs)) : 0.0; - color.blu= cap(1-1.5*lungs); + color.red= (0.2+lungs)*(0.4-lungs) + 25*(lungs-0.45)*(0.8-lungs); + color.gre= lungs>0.1 ? 2.45*(lungs)*(1.2-lungs) : 0.0; + color.blu= 1-1.5*lungs; return color; } Color3f GLView::setColorSpecies(float species) { Color3f color; - color.red= (cos((float)species/89*M_PI)+1.0)/2.0; - color.gre= (sin((float)species/54*M_PI)+1.0)/2.0; - color.blu= (cos((float)species/34*M_PI)+1.0)/2.0; + color.red= (cos((float)species/178*M_PI)+1.0)/2.0; + color.gre= (sin((float)species/101*M_PI)+1.0)/2.0; + color.blu= (cos((float)species/67*M_PI)+1.0)/2.0; return color; } @@ -1811,17 +1872,18 @@ Color3f GLView::setColorCrossable(float species) Color3f color(0.7, 0.7, 0.7); //all agents look grey if unrelated or if none is selected, b/c then we don't have a reference - if(world->getSelectedAgent()>=0){ - float deviation= abs(species - world->agents[world->getSelectedAgent()].species); //species deviation check + int selectedindex = world->getSelectedAgentIndex(); + if(selectedindex>=0){ + float deviation= abs(species - world->agents[selectedindex].species); //species deviation check if (deviation==0) { //exact copies: cyan color.red= 0.2; color.gre= 0.9; color.blu= 0.9; - } else if (deviation<=world->agents[world->getSelectedAgent()].kinrange) { + } else if (deviation<=world->agents[selectedindex].kinrange) { //female-only reproducable relatives: navy blue color.red= 0; color.gre= 0; - } else if (deviation<=3*conf::MAXDEVIATION) { + } else if (deviation<=world->agents[selectedindex].kinrange + conf::VISUALIZE_RELATED_RANGE) { //possible relatives: purple color.gre= 0.4; } @@ -1832,65 +1894,98 @@ Color3f GLView::setColorCrossable(float species) Color3f GLView::setColorGenerocity(float give) { Color3f color; - float val= cap(abs(give)*10/world->FOODTRANSFER)*2/3; + float val= cap(abs(give)*10/world->GENEROCITY_RATE)*2/3; if(give>0) color.gre= val; else color.red= val; - if(abs(give)<0.0005) { color.blu= 0.5; color.gre= 0.25; } + if(abs(give)<0.001) { color.blu= 0.75; color.gre= 0.5; } return color; } -Color3f GLView::setColorRepCount(float repcount, bool asexual) +Color3f GLView::setColorStrength(float strength) { Color3f color; - float val= powf(cap(1-repcount*0.5), 2); - if(asexual) color.gre= val*0.5; - else color.blu= val; - - int mod= 12; - if(repcount<1) mod= 3; - if((int)(repcount*1000)%mod>=mod*0.5){ - if(repcount<0) { - color.red= 1.0; - color.gre= 1.0; - color.blu= 1.0; - } else { - if(asexual) { - color.blu= val; - color.gre= val; - } else color.gre= val; - } + color.red= cap(2-strength)/2; + color.gre= (cap(2*strength)+2*cap(strength))/4; + color.blu= strength; + return color; +} + +Color3f GLView::setColorRepType(int type) +{ + Color3f color; + if(type==RepType::ASEXUAL){ + color.gre= 1; + } else if(type==RepType::FEMALE){ + color.blu= 1; + } else if(type==RepType::MALE){ + color.red= 1; } + return color; +} +Color3f GLView::setColorRepCount(float repcount, int type) +{ + Color3f color= setColorRepType(type); + + float val= 0.25 + 0.75*powf(cap(1-repcount*0.25), 2); //repcounter of 4 starts getting brighter, but otherwise we always display a little color + color.red*= val; color.gre*= val; color.blu*= val; + + if(repcount < 3) { //repcounter of 3 starts getting brighter... + int mod= 12; + if(repcount<1) mod= 3; //repcounter of 1 starts blinking more rapidly + if((int)(abs(repcount)*1000)%mod>=mod*0.5){ + if(repcount < 0) { //negative repcounter indicates agent is ready to reproduce but something prevents it (sexprojection, health below minimum) + color.red= 1.0; + color.gre= 1.0; + color.blu= 1.0; + } else { + color.red*= 0.25; color.gre*= 0.25; color.blu*= 0.25; + } + } + } return color; } Color3f GLView::setColorMutations(float rate, float size) { Color3f color; - //red= fast rate, blue= slow rate, dimmer= small size, brighter= larger size - float intensity= 0.25+10*size; - color.red= cap(2*rate*size); - color.gre= intensity; - color.blu= cap((0.5-2*rate)*size); + color.red= sqrt(3*rate)+cap(size-0.25); + color.gre= 4*size + cap(2*rate-0.25); + color.blu= powf((float)4*size,0.25)+cap(rate-0.5); return color; } -Color3f GLView::setColorStrength(float strength) +Color3f GLView::setColorGeneration(int gen) { Color3f color; - if(strength>=0.5){ - color.red= sqrt(cap(+3-5*strength)); - color.gre= sqrt(cap(-5+5*strength)); - } else { - color.red= sqrt(cap(1-5*strength)); - color.gre= sqrt(cap(-1+5*strength)); + float relgen= cap((gen - world->STATlowestgen)*world->STATinvgenrange); + + if (gen==world->STAThighestgen && gen!=0) { color.red= 0.2; color.gre= 0.9; color.blu= 0.9; } + else { + color.red= powf(relgen,0.25); + color.gre= relgen; + color.blu= powf(relgen,4); } - color.blu= sqrt(cap(-3+5*strength)); return color; } +std::pair GLView::setColorEar(int index) +{ + float alp= 0; + Color3f color(0,0,0); + switch(index) { + case 0: color.gre= 1; alp= 0.1; break; + case 1: color.red= 1; alp= 0.1; break; + case 2: color.red= 0.3; color.blu= 1; color.gre= 0.3; alp= 0.15; break; + case 3: color.red= 1; color.gre= 1; color.blu= 1; alp= 0.08; break; + default: color.red= 0.4; color.gre= 0.4; color.blu= 0.4; alp= 0.01; break; + } + + return std::make_pair(color, alp); +} + //Cell layer colors @@ -1991,11 +2086,11 @@ Color3f GLView::setColorTempCell(int val) Color3f color(sqrt(cap(1.3*temp-0.2)), cap(1.2*(1-1.1*temp)), pow(1-temp,3)); //revert "temp" to "val" to fix for cell-based temp layer if(temp>0.3) { float blu_gre= cap(2.5*temp-0.9); - color.red= cap(color.red + cap(2.5*temp-1.25)); + color.red= color.red + cap(2.5*temp-1.25); if(temp>0.6) { - color.gre= cap(color.gre + cap(1.8-2*temp)); - } else color.gre= cap(color.gre + blu_gre); - color.blu= cap(color.blu - blu_gre); + color.gre= color.gre + cap(1.8-2*temp); + } else color.gre= color.gre + blu_gre; + color.blu= color.blu - blu_gre; } return color; } @@ -2005,9 +2100,9 @@ Color3f GLView::setColorLight(float val) Color3f color; //if moonlight is a thing, then set a lower limit if(world->MOONLIT && world->MOONLIGHTMULT!=1.0) { - color.red= world->SUN_RED*val>world->MOONLIGHTMULT ? cap(world->SUN_RED*val) : world->MOONLIGHTMULT; - color.gre= world->SUN_GRE*val>world->MOONLIGHTMULT ? cap(world->SUN_GRE*val) : world->MOONLIGHTMULT; - color.blu= world->SUN_BLU*val>world->MOONLIGHTMULT ? cap(world->SUN_BLU*val) : world->MOONLIGHTMULT; + color.red= world->SUN_RED*val>world->MOONLIGHTMULT ? world->SUN_RED*val : world->MOONLIGHTMULT; + color.gre= world->SUN_GRE*val>world->MOONLIGHTMULT ? world->SUN_GRE*val : world->MOONLIGHTMULT; + color.blu= world->SUN_BLU*val>world->MOONLIGHTMULT ? world->SUN_BLU*val : world->MOONLIGHTMULT; } else { color.red= cap(world->SUN_RED*val); color.gre= cap(world->SUN_GRE*val); @@ -2020,11 +2115,11 @@ Color3f GLView::setColorLight(float val) bool GLView::cullAtCoords(int x, int y) { - //determine if the object at the x and y WORLD coords can be rendered, returning TRUE - if(y > wHeight*0.5/scalemult-ytranslate + conf::CZ) return true; - if(x > wWidth*0.5/scalemult-xtranslate + conf::CZ) return true; - if(y < -wHeight*0.5/scalemult-ytranslate - conf::CZ) return true; - if(x < -wWidth*0.5/scalemult-xtranslate - conf::CZ) return true; + //determine if the object at the x and y WORLD coords can be rendered, returning TRUE if it should be CULLED + if(y > wHeight*0.5/scalemult-ytranslate + 2*conf::CZ) return true; + if(x > wWidth*0.5/scalemult-xtranslate + 2*conf::CZ) return true; + if(y < -wHeight*0.5/scalemult-ytranslate - 2*conf::CZ) return true; + if(x < -wWidth*0.5/scalemult-xtranslate - 2*conf::CZ) return true; return false; } @@ -2040,43 +2135,54 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) float r= agent.radius; float rp= agent.radius+2.8; - if ((live_agentsvis!=Visual::NONE && (live_hidedead==0 || agent.health>0)) || ghost) { - //first, calculate colors for indicators (NOTE: Indicator tags are gone now, consider removing these too if unneeded) - Color3f healthcolor= setColorHealth(agent.health); - Color3f stomachcolor= setColorStomach(agent.stomach); - Color3f discomfortcolor= setColorTempPref(agent.discomfort); - Color3f metabcolor= setColorMetabolism(agent.metabolism); - Color3f tonecolor= setColorTone(agent.tone); - Color3f lungcolor= setColorLungs(agent.lungs); - Color3f speciescolor= setColorSpecies(agent.species); - Color3f crossablecolor= setColorCrossable(agent.species); - Color3f generocitycolor= setColorGenerocity(agent.dhealth); + if ((live_agentsvis!=Visual::NONE && (live_hidedead==0 || !agent.isDead()) && (live_hidegenz==0 || agent.gencount>0)) || ghost) { + // don't render if visual set to NONE, or if we're hiding the agent for some reason glPushMatrix(); //switch to local position coordinates glTranslatef(x,y,0); - if(agent.id==world->getSelectedID() && !ghost){ + if(world->isAgentSelected(agent.id) && !ghost){ + + if(live_profilevis==Profile::EYES || world->isDebug()){ + //eyesight FOV's for the eyesight profile + for(int q=0;qMAX_SENSORY_DISTANCE*cos(aa), world->MAX_SENSORY_DISTANCE*sin(aa), 0); + aa-= agent.eyefov[q]/2; + } + glVertex3f(world->MAX_SENSORY_DISTANCE*cos(aa), world->MAX_SENSORY_DISTANCE*sin(aa), 0); + glColor4f(agent.in[Input::EYES+q*3],agent.in[Input::EYES+1+q*3],agent.in[Input::EYES+2+q*3],0.5); + glVertex3f(0,0,0); + glEnd(); + } + } if(world->isDebug()){ //draw DIST range on selected in DEBUG glBegin(GL_LINES); glColor3f(1,0,1); - drawOutlineRes(0, 0, world->DIST, ceil(scalemult)+6); + drawOutlineRes(0, 0, world->MAX_SENSORY_DISTANCE, ceil(scalemult)+6); glEnd(); //spike effective zone - if(agent.isSpikey(world->SPIKELENGTH)){ + if(agent.isSpikey(world->SPIKE_LENGTH)){ glBegin(GL_POLYGON); glColor4f(1,0,0,0.25); glVertex3f(0,0,0); - glVertex3f((r+agent.spikeLength*world->SPIKELENGTH)*cos(agent.angle+M_PI/8), - (r+agent.spikeLength*world->SPIKELENGTH)*sin(agent.angle+M_PI/8),0); + glVertex3f((r+agent.spikeLength*world->SPIKE_LENGTH)*cos(agent.angle+M_PI/8), + (r+agent.spikeLength*world->SPIKE_LENGTH)*sin(agent.angle+M_PI/8),0); glColor4f(1,0.25,0.25,0.75); - glVertex3f((r+agent.spikeLength*world->SPIKELENGTH)*cos(agent.angle), - (r+agent.spikeLength*world->SPIKELENGTH)*sin(agent.angle),0); + glVertex3f((r+agent.spikeLength*world->SPIKE_LENGTH)*cos(agent.angle), + (r+agent.spikeLength*world->SPIKE_LENGTH)*sin(agent.angle),0); glColor4f(1,0,0,0.25); - glVertex3f((r+agent.spikeLength*world->SPIKELENGTH)*cos(agent.angle-M_PI/8), - (r+agent.spikeLength*world->SPIKELENGTH)*sin(agent.angle-M_PI/8),0); + glVertex3f((r+agent.spikeLength*world->SPIKE_LENGTH)*cos(agent.angle-M_PI/8), + (r+agent.spikeLength*world->SPIKE_LENGTH)*sin(agent.angle-M_PI/8),0); glVertex3f(0,0,0); glEnd(); } @@ -2115,193 +2221,232 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) drawCircle(0,0,sharedist); glEnd(); } - - //debug eyesight FOV's - for(int q=0;qDIST*cos(aa), world->DIST*sin(aa), 0); - aa-= agent.eyefov[q]/2; - glVertex3f(world->DIST*cos(aa), world->DIST*sin(aa), 0); - aa-= agent.eyefov[q]/2; - glVertex3f(world->DIST*cos(aa), world->DIST*sin(aa), 0); - glColor4f(agent.in[Input::EYES+q*3],agent.in[Input::EYES+1+q*3],agent.in[Input::EYES+2+q*3],0.5); - glVertex3f(0,0,0); - glEnd(); - } } glTranslatef(-90,24+0.5*r,0); if(scalemult > .2){ - //draw profile(s) - float col; - float yy=15; - float xx=15; - float ss=16; - + //Draw one of the profilers based on the mode we have selected + if(live_profilevis==Profile::INOUT || live_profilevis==Profile::BRAIN){ - glTranslatef(-120,0,0); //translate x more to make room. Remember to translate back by inverse amount at the end of this block + float value; + float boxsize= 14; //x&y size of boxes + float xoffset= 1; //offsets for when "whitespace" is needed + float yoffset= 8; + float drawx = 0; //current draw positions + float drawy = 0; + + glTranslatef(-70,0,0); //translate x more to make room. Remember to translate back by inverse amount at the end of this block //Draw inputs and outputs in in/out mode AND brain mode glBegin(GL_QUADS); for (int j=0;j .7){ + //draw text initials on inputs if zoomed in close glEnd(); - if(j<=Input::xEYES && j%3==0){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "R", 1.0f, 0.0f, 0.0f); - } else if(j<=Input::xEYES && j%3==1){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "G", 0.0f, 1.0f, 0.0f); - } else if(j<=Input::xEYES && j%3==2){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "B", 0.0f, 0.0f, 1.0f); + float textx = drawx + boxsize/3; + float texty = drawy + boxsize*2/3; + + if(j>=Input::EYES && j<=Input::xEYES && j%3==0){ + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "R", 1.0f, 0.0f, 0.0f); + } else if(j>=Input::EYES && j<=Input::xEYES && j%3==1){ + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "G", 0.0f, 1.0f, 0.0f); + } else if(j>=Input::EYES && j<=Input::xEYES && j%3==2){ + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "B", 0.0f, 0.0f, 1.0f); } else if(j==Input::CLOCK1 || j==Input::CLOCK2 || j==Input::CLOCK3){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "Q", 0.0f, 0.0f, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "Q", 0.0f, 0.0f, 0.0f); } else if(j==Input::TEMP){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "T", col,(2-col)/2,(1-col)); - } else if(j==Input::HEARING1 || j==Input::HEARING2){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "E", 1.0f, 1.0f, 1.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "T", value,(2-value)/2,(1-value)); + } else if(j>=Input::EARS && j<=Input::xEARS){ + std::pair earcolor= setColorEar(j-Input::EARS); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "E", earcolor.first.red, earcolor.first.gre, earcolor.first.blu); } else if(j==Input::HEALTH){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "H", healthcolor.red, healthcolor.gre, healthcolor.blu); + Color3f healthcolor= setColorHealth(agent.health); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "H", healthcolor.red, healthcolor.gre, healthcolor.blu); + } else if(j==Input::REPCOUNT){ + Color3f repcountcolor= setColorRepCount(agent.repcounter, agent.getRepType()); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "R", cap(0.2+0.8*repcountcolor.red), cap(0.2+0.8*repcountcolor.gre), cap(0.2+0.8*repcountcolor.blu)); } else if(j==Input::BLOOD){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "B", 0.6f, 0.0, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "B", 0.6f, 0.0, 0.0f); } else if(j==Input::FRUIT_SMELL){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 1.0f, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 1.0f, 0.0f); } else if(j==Input::HAZARD_SMELL){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "S", 0.9f, 0.0f, 0.81f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "S", 0.9f, 0.0f, 0.81f); } else if(j==Input::MEAT_SMELL){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 0.0f, 0.1f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 0.0f, 0.1f); } else if(j==Input::WATER_SMELL){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "S", 0.3f, 0.3f, 0.9f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "S", 0.3f, 0.3f, 0.9f); } else if(j==Input::BOT_SMELL){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 1.0f, 1.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 1.0f, 1.0f); } else if(j==Input::PLAYER){ - RenderString(xx/3+ss*j, yy*2/3, GLUT_BITMAP_HELVETICA_12, "+", 1.0f, 1.0f, 1.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "+", 1.0f, 1.0f, 1.0f); + } else if(j==Input::RANDOM){ + float col = randf(0,1); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "?", col, col, col); } glBegin(GL_QUADS); } + + //set up for next draw location + drawx+= boxsize + xoffset; + if ((j+1)%conf::INPUTS_OUTPUTS_PER_ROW==0) { //if we over-ran the x-axis, reset x and move y to start next row + drawx= 0; + drawy+= boxsize + xoffset; + } + } + + drawy+= boxsize + yoffset; + + if(live_profilevis==Profile::BRAIN){ + //draw brain in brain profile mode, complements the i/o as drawn above and below + boxsize= 8; + drawx= 0; + + for (int j=0;j .7){ glEnd(); + float textx = drawx + boxsize/3; + float texty = drawy + boxsize*2/3; + if(j==Output::LEFT_WHEEL_B || j==Output::LEFT_WHEEL_F){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "!", 1.0f, 0.0f, 1.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "!", 1.0f, 0.0f, 1.0f); } else if(j==Output::RIGHT_WHEEL_B || j==Output::RIGHT_WHEEL_F){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "!", 0.0f, 1.0f, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "!", 0.0f, 1.0f, 0.0f); } else if(j==Output::VOLUME){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "V", 1.0f, 1.0f, 1.0f); + float compcol = value>0.75 ? 0.0f : 1.0f; + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "V", compcol, compcol, compcol); } else if(j==Output::TONE){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "T", tonecolor.red, tonecolor.gre, tonecolor.blu); + Color3f tonecolor= setColorTone(agent.tone); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "T", tonecolor.red, tonecolor.gre, tonecolor.blu); } else if(j==Output::CLOCKF3){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "Q", 0.0f, 0.0f, 0.0f); + float compcol = value>0.75 ? 0.0f : 1.0f; + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "Q", compcol, compcol, compcol); } else if(j==Output::SPIKE){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 0.0f, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "S", 1.0f, 0.0f, 0.0f); } else if(j==Output::PROJECT){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "P", 0.5f, 0.0f, 0.5f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "X", 0.6f, 0.0f, 0.6f); } else if(j==Output::JAW){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, ">", 1.0f, 1.0f, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, ">", 1.0f, 1.0f, 0.0f); } else if(j==Output::GIVE){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "G", 0.0f, 0.3f, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "G", 0.0f, 0.3f, 0.0f); } else if(j==Output::GRAB){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "G", 0.0f, 0.6f, 0.6f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "G", 0.0f, 0.6f, 0.6f); } else if(j==Output::GRAB_ANGLE){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "<", 0.0f, 0.6f, 0.6f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "<", 0.0f, 0.6f, 0.6f); } else if(j==Output::JUMP){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "J", 1.0f, 1.0f, 0.0f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "J", 1.0f, 1.0f, 0.0f); } else if(j==Output::BOOST){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "B", 0.6f, 0.6f, 0.6f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "B", 0.6f, 0.6f, 0.6f); + } else if(j==Output::STIMULANT){ + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "$", 0.0f, 1.0f, 1.0f); } else if(j==Output::WASTE_RATE){ - RenderString(xx/3+ss*j, yy+ss*2/3, GLUT_BITMAP_HELVETICA_12, "W", 0.9f, 0.0f, 0.81f); + RenderString(textx, texty, GLUT_BITMAP_HELVETICA_12, "W", 0.9f, 0.0f, 0.81f); } glBegin(GL_QUADS); } - + drawx+= boxsize + xoffset; + if ((j+1)%conf::INPUTS_OUTPUTS_PER_ROW==0) { + drawx= 0; + drawy+= boxsize + xoffset; + } } - yy+=ss*2; glEnd(); - glTranslatef(120,0,0); //translate back for brain displays (see top of this block) + glTranslatef(70,0,0); //translate back for brain displays (see top of this block) } - //Draw one of the profilers based on the mode we have selected - if(live_profilevis==Profile::BRAIN){ - //draw brain in brain profile mode, complements the i/o as drawn above - glBegin(GL_QUADS); - float offx=0; - ss=8; - xx=ss; - for (int j=0;j earcolor= setColorEar(q); + + glColor4f(earcolor.first.red, earcolor.first.gre, earcolor.first.blu, earcolor.second); //Draw a trapezoid indicating the full range the ear hears, including the limbs float displace= (agent.hearhigh[q]-agent.hearlow[q])*0.5; - if(displace>world->SOUNDPITCHRANGE) displace= world->SOUNDPITCHRANGE; - float heightratio= 1-cap(displace/world->SOUNDPITCHRANGE); //when displace= the SOUNDPITCHRANGE, + if(displace>world->SOUND_PITCH_RANGE) displace= world->SOUND_PITCH_RANGE; + float heightratio= 1-cap(displace*world->INV_SOUND_PITCH_RANGE); //when displace= the SOUND_PITCH_RANGE, - glVertex3f(2+176*cap(agent.hearlow[q]+displace), 2+78*heightratio, 0); //top-left L _______ H L H + glVertex3f(2+176*cap(agent.hearlow[q]+displace), 2+78*heightratio, 0); //top-left L _______ H L___H glVertex3f(2+176*cap(agent.hearlow[q]), 78, 0); // bottom-left /| |\* | glVertex3f(2+176*cap(agent.hearhigh[q]), 78, 0); //bottom-right / |h | \* but also /|\* glVertex3f(2+176*cap(agent.hearhigh[q]-displace), 2+78*heightratio, 0); // top-right /_d|_____|__\* /d|_\* @@ -2342,11 +2487,31 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) if(agent.isTiny()) break; //only one ear for tiny agents } - glEnd(); + glEnd(); + + //top corner labels + if(scalemult > .7){ + RenderString(8, 8, GLUT_BITMAP_HELVETICA_12, "low", 0.7f, 0.7f, 0.7f); + RenderString(180-8-22, 8, GLUT_BITMAP_HELVETICA_12, "high", 0.7f, 0.7f, 0.7f); + } + + //box outline + glBegin(GL_LINES); + glColor3f(0.8,0.8,0.8); + glVertex3f(0, 0, 0); + glVertex3f(0, 80, 0); + glVertex3f(0, 80, 0); + glVertex3f(180, 80, 0); + glVertex3f(180, 80, 0); + glVertex3f(180, 0, 0); + glVertex3f(180, 0, 0); + glVertex3f(0, 0, 0); + glEnd(); //now show our own sound, colored by tone glLineWidth(3); glBegin(GL_LINES); + Color3f tonecolor= setColorTone(agent.tone); glColor3f(tonecolor.red, tonecolor.gre, tonecolor.blu); glVertex3f(2+176*agent.tone, 78, 0); glVertex3f(2+176*agent.tone, 78-76*agent.volume, 0); @@ -2364,13 +2529,14 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) if(volume>=1) volume= cap(volume-1.0); else fiz= 0.3; - if(tone==0.25) glColor4f(0.0,0.8,0.0,fiz); //this is the wheel sounds of other agents + //display half-lime, half-magenta line for the wheel sounds of others + if(tone==conf::WHEEL_TONE) glColor4f(0.0,0.8,0.0,fiz); else glColor4f(0.7,0.7,0.7,fiz); glVertex3f(2+176*tone, 78, 0); glVertex3f(2+176*tone, 78-76*volume, 0); - if(tone==0.25){ //display half-magenta line for the wheel sounds of others + if(tone==conf::WHEEL_TONE){ glColor4f(0.8,0.0,0.8,fiz); glVertex3f(2+176*tone, 78, 0); glVertex3f(2+176*tone, 78-38*volume, 0); @@ -2390,8 +2556,8 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) glBegin(GL_LINES); float offx=0; ss=30; - xx=ss; - for (int j=0;jconf::RENDER_MINVOLUME){ + Color3f tonecolor= setColorTone(agent.tone); if(scalemult > 1) glLineWidth(3); else if(scalemult > .3) glLineWidth(2); @@ -2462,13 +2636,13 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) float count= agent.tone*15+1; for (int l=0; l<=(int)count; l++){ - float dist= world->DIST*(l/count) + 2*(world->modcounter%(int)(world->DIST/2)); - if (dist>world->DIST) dist-= world->DIST; - float distfratio= dist/world->DIST; + float dist= world->MAX_SENSORY_DISTANCE*(l/count) + 2*(world->modcounter%(int)(world->MAX_SENSORY_DISTANCE/2)); + if (dist>world->MAX_SENSORY_DISTANCE) dist-= world->MAX_SENSORY_DISTANCE; + float distfratio= dist*world->INV_MAX_SENSORY_DISTANCE; //this dist func works in 3 parts: first, we give it a displacement based on the l-th ring / count //then, we take modcounter, and divide it by the distance, giving us a proportion of the radius //The weird *2 and /2 bits are to increase the speed of the waves. Leave them - //finally, if the dist is too large, wrap it down by subtracting DIST. And we take a ratio value too to use later + //finally, if the dist is too large, wrap it down by subtracting MAX_SENSORY_DISTANCE. And we take a ratio value too to use later glBegin(GL_LINES); glColor4f(tonecolor.red, tonecolor.gre, tonecolor.blu, cap((conf::RENDER_MINVOLUME+1.2*volume-distfratio))); drawOutlineRes(0, 0, dist, (int)(ceil(2*scalemult)+1+5*distfratio)); @@ -2497,9 +2671,24 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) glVertex3f(xo+5,yo+42,0); glVertex3f(xo,yo+42,0); - //repcounter/energy + //energy + xo+= 7; + glColor3f(0,0,0); + glVertex3f(xo,yo,0); + glVertex3f(xo+5,yo,0); + glVertex3f(xo+5,yo+42,0); + glVertex3f(xo,yo+42,0); + + float exh= 2/(1+exp(agent.exhaustion)); + glColor3f(0.8*exh,0.8*exh,cap(1-agent.exhaustion)*0.75); + glVertex3f(xo,yo+42*(1-exh),0); + glVertex3f(xo+5,yo+42*(1-exh),0); + glColor3f(0.8,0.8,0); + glVertex3f(xo+5,yo+42,0); + glVertex3f(xo,yo+42,0); + + //repcounter xo+= 7; - glBegin(GL_QUADS); glColor3f(0,0,0); glVertex3f(xo,yo,0); glVertex3f(xo+5,yo,0); @@ -2512,6 +2701,8 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) glColor3f(0,0.5,0.6); glVertex3f(xo+5,yo+42,0); glVertex3f(xo,yo+42,0); + + //end side-by-side displays glEnd(); if(world->isDebug()) { @@ -2535,7 +2726,7 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) //end local coordinate stuff glPopMatrix(); - if(agent.id==world->getSelectedID() && !ghost) {//extra info for selected agent + if(world->isAgentSelected(agent.id) && !ghost) {//extra info for selected agent //debug stuff if(world->isDebug()) { //debug sight lines: connect to anything selected agent sees @@ -2554,10 +2745,11 @@ void GLView::drawPreAgent(const Agent& agent, float x, float y, bool ghost) int scx= (int) (agent.pos.x/conf::CZ); int scy= (int) (agent.pos.y/conf::CZ); - minx= (scx-world->DIST/conf::CZ/2) > 0 ? (scx-world->DIST/conf::CZ/2)*conf::CZ : 0; - maxx= (scx+1+world->DIST/conf::CZ/2) < conf::WIDTH/conf::CZ ? (scx+1+world->DIST/conf::CZ/2)*conf::CZ : conf::WIDTH; - miny= (scy-world->DIST/conf::CZ/2) > 0 ? (scy-world->DIST/conf::CZ/2)*conf::CZ : 0; - maxy= (scy+1+world->DIST/conf::CZ/2) < conf::HEIGHT/conf::CZ ? (scy+1+world->DIST/conf::CZ/2)*conf::CZ : conf::HEIGHT; + float distmult= conf::SMELL_DIST_MULT/(float)conf::CZ/2; + minx= (scx-world->MAX_SENSORY_DISTANCE*distmult) > 0 ? (scx-world->MAX_SENSORY_DISTANCE*distmult)*conf::CZ : 0; + maxx= (scx+1+world->MAX_SENSORY_DISTANCE*distmult) < conf::WIDTH/conf::CZ ? (scx+1+world->MAX_SENSORY_DISTANCE*distmult)*conf::CZ : conf::WIDTH; + miny= (scy-world->MAX_SENSORY_DISTANCE*distmult) > 0 ? (scy-world->MAX_SENSORY_DISTANCE*distmult)*conf::CZ : 0; + maxy= (scy+1+world->MAX_SENSORY_DISTANCE*distmult) < conf::HEIGHT/conf::CZ ? (scy+1+world->MAX_SENSORY_DISTANCE*distmult)*conf::CZ : conf::HEIGHT; glColor3f(0,1,0); glVertex3f(minx,miny,0); @@ -2595,7 +2787,7 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) glTranslatef(x,y,0); //handle selected agent outliner - if (live_agentsvis!=Visual::NONE && agent.id==world->getSelectedID() && !ghost) { + if (live_agentsvis!=Visual::NONE && world->isAgentSelected(agent.id) && !ghost) { //draw selection outline glLineWidth(2); glBegin(GL_LINES); @@ -2605,56 +2797,39 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) glLineWidth(1); } - if ((live_agentsvis!=Visual::NONE && (live_hidedead==0 || agent.health>0)) || ghost) { - // don't render if visual set to NONE, or if agent is dead and we're hiding dead + if ((live_agentsvis!=Visual::NONE && (live_hidedead==0 || !agent.isDead())) && (live_hidegenz==0 || agent.gencount>0) || ghost) { + // don't render if visual set to NONE, or if we're hiding the agent for some reason //agent body color assignment - float red= 0,gre= 0,blu= 0; - - //first, calculate colors - Color3f healthcolor= setColorHealth(agent.health); - Color3f stomachcolor= setColorStomach(agent.stomach); - Color3f discomfortcolor= setColorTempPref(agent.discomfort); - Color3f metabcolor= setColorMetabolism(agent.metabolism); - Color3f tonecolor= setColorTone(agent.tone); - Color3f lungcolor= setColorLungs(agent.lungs); - Color3f speciescolor= setColorSpecies(agent.species); - Color3f crossablecolor= setColorCrossable(agent.species); - Color3f generocitycolor= setColorGenerocity(agent.dhealth); -// Color3f sexualitycolor= setColorSexuality(agent); - Color3f repcountcolor= setColorRepCount(agent.repcounter, agent.isAsexual()); - - //now colorize agents and other things - if (live_agentsvis==Visual::RGB){ //real rgb values - red= agent.real_red; gre= agent.real_gre; blu= agent.real_blu; - + Color3f color; + if (live_agentsvis==Visual::RGB || (live_agentsvis==Visual::NONE && ghost)){ //real rgb values + color= Color3f(agent.real_red, agent.real_gre, agent.real_blu); } else if (live_agentsvis==Visual::STOMACH){ - red= stomachcolor.red; gre= stomachcolor.gre; blu= stomachcolor.blu; - + color= setColorStomach(agent.stomach); } else if (live_agentsvis==Visual::DISCOMFORT){ - red= discomfortcolor.red; gre= discomfortcolor.gre; blu= discomfortcolor.blu; - + color= setColorTempPref(agent.discomfort); } else if (live_agentsvis==Visual::VOLUME) { - red= agent.volume; gre= agent.volume; blu= agent.volume; - + color= Color3f(agent.volume); } else if (live_agentsvis==Visual::SPECIES){ - red= speciescolor.red; gre= speciescolor.gre; blu= speciescolor.blu; - + color= setColorSpecies(agent.species); } else if (live_agentsvis==Visual::CROSSABLE){ //crossover-compatable to selection - red= crossablecolor.red; gre= crossablecolor.gre; blu= crossablecolor.blu; - + color= setColorCrossable(agent.species); } else if (live_agentsvis==Visual::HEALTH) { - red= healthcolor.red; gre= healthcolor.gre; //blu= healthcolor.blu; - + color= setColorHealth(agent.health); } else if (live_agentsvis==Visual::REPCOUNTER) { - red= repcountcolor.red; gre= repcountcolor.gre; blu= repcountcolor.blu; - + color= setColorRepCount(agent.repcounter, agent.getRepType()); } else if (live_agentsvis==Visual::METABOLISM){ - red= metabcolor.red; gre= metabcolor.gre; blu= metabcolor.blu; - + color= setColorMetabolism(agent.metabolism); + } else if (live_agentsvis==Visual::STRENGTH){ + color= setColorStrength(agent.strength); + } else if (live_agentsvis==Visual::BRAINMUTATION){ + color= setColorMutations(agent.brain_mutation_chance, agent.brain_mutation_size); + } else if (live_agentsvis==Visual::GENEMUTATION){ + color= setColorMutations(agent.gene_mutation_chance, agent.gene_mutation_size); } else if (live_agentsvis==Visual::LUNGS){ - red= lungcolor.red; gre= lungcolor.gre; blu= lungcolor.blu; - + color= setColorLungs(agent.lungs); + } else if (live_agentsvis==Visual::GENERATIONS){ + color= setColorGeneration(agent.gencount); } //the center gets its own alpha and darkness multiplier. For now, tied to reproduction mode (asexual, sexual F, or sexual M). See centerrender def @@ -2672,14 +2847,14 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) } - if (agent.health<=0 || (scalemult <= 0.3 && !ghost)) { + if (agent.isDead() || (scalemult <= 0.3 && !ghost) || live_agentsvis!=Visual::RGB) { //when zoomed out too far to want to see agent details, or if agent is dead (either ghost render or not), draw center as solid centeralpha= 1; centercmult= 1; } //mult for dead agents, to display more uniform & transparent parts - float dead= agent.health==0 ? conf::RENDER_DEAD_ALPHA : 1; + float dead= agent.isDead() ? conf::RENDER_DEAD_ALPHA : 1; //we are now ready to start drawing @@ -2691,10 +2866,7 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) float aa= agent.angle+agent.eyedir[q]; glBegin(GL_LINES); - if (live_profilevis==Profile::EYES) { - //color eye lines based on actual input if we're on the eyesight profile - glColor4f(agent.in[Input::EYES+q*3],agent.in[Input::EYES+1+q*3],agent.in[Input::EYES+2+q*3],0.75*dead); - } else glColor4f(0.5,0.5,0.5,0.75*dead); + glColor4f(agent.in[Input::EYES+q*3],agent.in[Input::EYES+1+q*3],agent.in[Input::EYES+2+q*3],0.75*dead); glVertex3f(r*cos(aa), r*sin(aa),0); glVertex3f((2*r+30)*cos(aa), (2*r+30)*sin(aa), 0); //this draws whiskers. Don't get rid of or change these <3 @@ -2707,14 +2879,19 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) glBegin(GL_POLYGON); float aa= agent.angle + agent.eardir[q]; //the centers of ears are black - glColor4f(0,0,0,0.5); - glVertex3f(r*cos(aa), r*sin(aa),0); + glColor4f(0,0,0,0.35); + glVertex3f(rad*cos(aa), rad*sin(aa),0); + //color ears differently if we are set on the sound profile or debug - if(live_profilevis==Profile::SOUND || world->isDebug()) glColor4f(1-(q/(NUMEARS-1)),q/(NUMEARS-1),0,0.75*dead); - else glColor4f(agent.real_red,agent.real_gre,agent.real_blu,0.5*dead); - drawCircle(r*cos(aa), r*sin(aa), 2*agent.hear_mod); - glColor4f(0,0,0,0.5); - glVertex3f(r*cos(aa), r*sin(aa),0); + if(live_profilevis==Profile::SOUND || world->isDebug()) { + std::pair earcolor= setColorEar(q); + + glColor4f(earcolor.first.red, earcolor.first.gre, earcolor.first.blu, 0.5*dead); + } else glColor4f(agent.real_red,agent.real_gre,agent.real_blu,0.5*dead); + + drawCircle(rad*cos(aa), rad*sin(aa), 2*agent.hear_mod); + glColor4f(0,0,0,0.35); + glVertex3f(rad*cos(aa), rad*sin(aa),0); glEnd(); if(agent.isTiny()) break; //tiny agents have only one ear } @@ -2725,11 +2902,11 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) Vector2f displace= agent.dpos - agent.pos; for(int q=1;q<4;q++){ glBegin(GL_POLYGON); - glColor4f(red,gre,blu,dead*centeralpha*0.1); + glColor4f(color.red,color.gre,color.blu,dead*centeralpha*0.1); glVertex3f(0,0,0); - glColor4f(red,gre,blu,0.25); + glColor4f(color.red,color.gre,color.blu,0.25); drawCircle(capm(displace.x, -1, 1)*(q*10), capm(displace.y, -1, 1)*(q*10), r); - glColor4f(red,gre,blu,dead*centeralpha*0.1); + glColor4f(color.red,color.gre,color.blu,dead*centeralpha*0.1); glVertex3f(0,0,0); glEnd(); } @@ -2762,11 +2939,11 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) //body glBegin(GL_POLYGON); - glColor4f(red*centercmult,gre*centercmult,blu*centercmult,dead*centeralpha); + glColor4f(color.red*centercmult,color.gre*centercmult,color.blu*centercmult,dead*centeralpha); glVertex3f(0,0,0); - glColor4f(red,gre,blu,dead); + glColor4f(color.red,color.gre,color.blu,dead); drawCircleRes(0, 0, rad, getAgentRes(ghost)); - glColor4f(red*centercmult,gre*centercmult,blu*centercmult,dead*centeralpha); + glColor4f(color.red*centercmult,color.gre*centercmult,color.blu*centercmult,dead*centeralpha); glVertex3f(0,0,0); glEnd(); @@ -2782,12 +2959,15 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) if ((scalemult > .08 || ghost) && agent.jawrend>0) { //dont render jaws if zoomed too far out, but always render them on ghosts, and only if they've been active within the last few ticks glColor4f(0.9,0.9,0,blur); - float mult= 1-powf(abs(agent.jawPosition),0.5); - if(agent.jawrend==conf::JAWRENDERTIME) mult= 1; //at the start of the timer the jaws are rendered open for aesthetic + + float jawangle = ( 1 - powf(abs(agent.jawPosition), 0.5) ) * M_PI/8; + if(agent.jawrend == conf::JAWRENDERTIME) jawangle = M_PI/8; //at the start of the timer the jaws are rendered open for aesthetic + float jawlength = world->BITE_DISTANCE - 2; //shave just a little bit off the jaw length + glVertex3f(rad*cos(agent.angle), rad*sin(agent.angle), 0); - glVertex3f((10+rad)*cos(agent.angle+M_PI/8*mult), (10+rad)*sin(agent.angle+M_PI/8*mult), 0); + glVertex3f((jawlength+rad)*cos(agent.angle+jawangle), (jawlength+rad)*sin(agent.angle+jawangle), 0); glVertex3f(rad*cos(agent.angle), rad*sin(agent.angle), 0); - glVertex3f((10+rad)*cos(agent.angle-M_PI/8*mult), (10+rad)*sin(agent.angle-M_PI/8*mult), 0); + glVertex3f((jawlength+rad)*cos(agent.angle-jawangle), (jawlength+rad)*sin(agent.angle-jawangle), 0); } glEnd(); @@ -2798,16 +2978,16 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) out_gre= 1.0; out_blu= 1.0; } - if (agent.isAirborne()) { //draw jumping as a thick outline no matter what + if (agent.isAirborne() && scalemult > scale4ksupport) { //draw jumping as a thick outline no matter what glLineWidth(3); } glBegin(GL_LINES); - if (live_agentsvis!=Visual::HEALTH && agent.health<=0){ //draw outline as grey if dead, unless visualizing health + if (live_agentsvis!=Visual::HEALTH && agent.isDead()){ //draw outline as grey if dead, unless visualizing health glColor4f(0.7,0.7,0.7,dead); //otherwise, color as agent's body if zoomed out, color as above if normal zoomed - } else glColor4f(cap(out_red*blur + (1-blur)*red), cap(out_gre*blur + (1-blur)*gre), cap(out_blu*blur + (1-blur)*blu), dead); + } else glColor4f(cap(out_red*blur + (1-blur)*color.red), cap(out_gre*blur + (1-blur)*color.gre), cap(out_blu*blur + (1-blur)*color.blu), dead); drawOutlineRes(0, 0, rad, getAgentRes(ghost)); glEnd(); glLineWidth(1); @@ -2817,12 +2997,12 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) if(scalemult > .1 || ghost){ //spike, but only if sticking out - if (agent.isSpikey(world->SPIKELENGTH)) { + if (agent.isSpikey(world->SPIKE_LENGTH)) { glBegin(GL_LINES); glColor4f(0.7,0,0,blur); glVertex3f(0,0,0); - glVertex3f((world->SPIKELENGTH*agent.spikeLength)*cos(agent.angle), - (world->SPIKELENGTH*agent.spikeLength)*sin(agent.angle), + glVertex3f((world->SPIKE_LENGTH*agent.spikeLength)*cos(agent.angle), + (world->SPIKE_LENGTH*agent.spikeLength)*sin(agent.angle), 0); glEnd(); } @@ -2875,19 +3055,27 @@ void GLView::drawAgent(const Agent& agent, float x, float y, bool ghost) if(agent.isTiny() && !agent.isTinyEye(q)) break; if( (world->modcounter+agent.id)%conf::BLINKDELAY>=q*10 && (world->modcounter+agent.id)%conf::BLINKDELAY0) continue; //blink eyes ocasionally... DAWWWWWW + !agent.isDead()) continue; //blink eyes ocasionally... DAWWWWWW + + float ca= agent.angle+agent.eyedir[q]; + + glBegin(GL_POLYGON); + glColor4f(0.85,0.85,0.85,1.0); + //whites of eyes indicate a > eye cell modifier + float eyewhitesize= capm(agent.eye_see_cell_mod/2,0.1,2); + drawCircle((rad-1-eyewhitesize/2)*cos(ca), (rad-1-eyewhitesize/2)*sin(ca), eyewhitesize); + glEnd(); glBegin(GL_POLYGON); glColor4f(0,0,0,1.0); - float ca= agent.angle+agent.eyedir[q]; //the eyes are small and tiny if the agent has a low eye mod. otherwise they are big and buggie - float eyesize= capm(agent.eye_see_agent_mod/2,0.1,1); - drawCircle((rad-2+eyesize)*cos(ca), (rad-2+eyesize)*sin(ca), eyesize); + float eyesize= capm(agent.eye_see_agent_mod/2,0.1,2); + drawCircle((rad-1-eyesize/2)*cos(ca), (rad-1-eyesize/2)*sin(ca), eyesize); glEnd(); } //render grab target line if we have one - if(world->GRAB_PRESSURE!=0 && agent.isGrabbing() && agent.health>0 && agent.grabID!=-1 && !ghost){ + if(world->GRAB_PRESSURE!=0 && agent.isGrabbing() && !agent.isDead() && agent.grabID!=-1 && !ghost){ glLineWidth(3); glBegin(GL_LINES); @@ -2954,7 +3142,7 @@ void GLView::drawData() //print global agent stats in top-left region int spaceperline= 14; - if(!cullAtCoords(0,-ytranslate)){ //don't do this if we can't see the left side + if(!cullAtCoords(0,-ytranslate) || !cullAtCoords(-UID::GRAPHWIDTH,-ytranslate)){ //don't do this if we can't see the left side /*if(scalemult>0.065){ for(int i= 0; iMAXPOP+400 > 500 ? world->MAXPOP+400 : 500; + float yscaling = conf::HEIGHT / worldpop; + float xinterval = (float)UID::GRAPHWIDTH / world->REPORTS_PER_EPOCH; + int agentinterval = yscaling*100; - //start graphs of aquatics, amphibians, and terrestrials - Color3f graphcolor= setColorLungs(0.0); - glBegin(GL_QUADS); - - //aquatic count - glColor3f(graphcolor.red,graphcolor.gre,graphcolor.blu); - for(int q=0;qptr-1){ - glColor3f((1+graphcolor.red)/2,(1+graphcolor.gre)/2,(1+graphcolor.blu)/2); - continue; - } - glVertex3f(-2010 + q*10, conf::HEIGHT - mm*world->numTotal[q],0); - glVertex3f(-2010 +(q+1)*10, conf::HEIGHT - mm*world->numTotal[q+1],0); - glVertex3f(-2010 +(q+1)*10, conf::HEIGHT, 0); - glVertex3f(-2010 + q*10, conf::HEIGHT, 0); + //Start with gridlines: horizontal: every 100 agents, vertical: days and epochs, scrolling with data + glBegin(GL_LINES); + glColor3f(0.8,0.8,0.8); + + int debugcountvert = 0; + for(int n=0;nREPORTS_PER_EPOCH;n++) { + if(world->graphGuides[n]==GuideLine::NONE) continue; + else if (world->graphGuides[n]==GuideLine::EPOCH) glColor3f(0.8,0.4,0.3); + glVertex3f(-UID::GRAPHWIDTH + n*xinterval, 0, 0); + glVertex3f(-UID::GRAPHWIDTH + n*xinterval, conf::HEIGHT, 0); + debugcountvert++; + glColor3f(0.8,0.8,0.8); } - //amphibian count - graphcolor= setColorLungs(0.4); - glColor3f(graphcolor.red,graphcolor.gre,graphcolor.blu); - for(int q=0;qptr-1){ - glColor3f((1+graphcolor.red)/2,(1+graphcolor.gre)/2,(1+graphcolor.blu)/2); - continue; - } - glVertex3f(-2010 + q*10, conf::HEIGHT - mm*world->numAmphibious[q] - mm*world->numTerrestrial[q],0); - glVertex3f(-2010 +(q+1)*10, conf::HEIGHT - mm*world->numAmphibious[q+1] - mm*world->numTerrestrial[q+1],0); - glVertex3f(-2010 +(q+1)*10, conf::HEIGHT, 0); - glVertex3f(-2010 + q*10, conf::HEIGHT, 0); + int debugcounthoriz = 0; + for(int n=0; n<((float)conf::HEIGHT/agentinterval); n++) { + //if(cullAtCoords(-xtranslate,(-n*agentinterval)*scalemult)) break; + debugcounthoriz++; + glVertex3f(0, conf::HEIGHT - n*agentinterval, 0); + glVertex3f(-UID::GRAPHWIDTH, conf::HEIGHT - n*agentinterval, 0); } - //terrestrial count - graphcolor= setColorLungs(1.0); - glColor3f(graphcolor.red,graphcolor.gre,graphcolor.blu); - for(int q=0;qptr-1){ - glColor3f((1+graphcolor.red)/2,(1+graphcolor.gre)/2,(1+graphcolor.blu)/2); - continue; - } - glVertex3f(-2010 + q*10, conf::HEIGHT - mm*world->numTerrestrial[q],0); - glVertex3f(-2010 +(q+1)*10, conf::HEIGHT - mm*world->numTerrestrial[q+1],0); - glVertex3f(-2010 +(q+1)*10, conf::HEIGHT, 0); - glVertex3f(-2010 + q*10, conf::HEIGHT, 0); - } + //printf("there were %i vertical and %i horizontal lines drawn\n",debugcountvert,debugcounthoriz); glEnd(); + + //Render data + for(int q=0;qREPORTS_PER_EPOCH-1;q++) { + if(world->numTotal[q]==0) continue; + + //start graphs of aquatics, amphibians, and terrestrials + Color3f graphcolor= setColorLungs(0.0); + glBegin(GL_QUADS); + + //aquatic count + glColor3f(graphcolor.red,graphcolor.gre,graphcolor.blu); + glVertex3f(-UID::GRAPHWIDTH + q*xinterval, conf::HEIGHT - yscaling*world->numTotal[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval, conf::HEIGHT - yscaling*world->numTotal[q+1],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval, conf::HEIGHT, 0); + glVertex3f(-UID::GRAPHWIDTH + q*xinterval, conf::HEIGHT, 0); + + //amphibian count + graphcolor= setColorLungs(0.4); + glColor3f(graphcolor.red,graphcolor.gre,graphcolor.blu); + glVertex3f(-UID::GRAPHWIDTH + q*xinterval, conf::HEIGHT - yscaling*world->numAmphibious[q] - yscaling*world->numTerrestrial[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval, conf::HEIGHT - yscaling*world->numAmphibious[q+1] - yscaling*world->numTerrestrial[q+1],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval, conf::HEIGHT, 0); + glVertex3f(-UID::GRAPHWIDTH + q*xinterval, conf::HEIGHT, 0); + + //terrestrial count + graphcolor= setColorLungs(1.0); + glColor3f(graphcolor.red,graphcolor.gre,graphcolor.blu); + glVertex3f(-UID::GRAPHWIDTH + q*xinterval, conf::HEIGHT - yscaling*world->numTerrestrial[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval, conf::HEIGHT - yscaling*world->numTerrestrial[q+1],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval, conf::HEIGHT, 0); + glVertex3f(-UID::GRAPHWIDTH + q*xinterval, conf::HEIGHT, 0); + + glEnd(); - glBegin(GL_LINES); - glColor3f(0,0,0.5); //hybrid count - for(int q=0;qptr-1) continue; - glVertex3f(-2010 + q*10, conf::HEIGHT - mm*world->numHybrid[q],0); - glVertex3f(-2010 +(q+1)*10, conf::HEIGHT - mm*world->numHybrid[q+1],0); - } - glColor3f(0,1,0); //herbivore count - for(int q=0;qptr-1) continue; - glVertex3f(-2010 + q*10,conf::HEIGHT - mm*world->numHerbivore[q],0); - glVertex3f(-2010 +(q+1)*10,conf::HEIGHT - mm*world->numHerbivore[q+1],0); - } - glColor3f(1,0,0); //carnivore count - for(int q=0;qptr-1) continue; - glVertex3f(-2010 + q*10,conf::HEIGHT - mm*world->numCarnivore[q],0); - glVertex3f(-2010 +(q+1)*10,conf::HEIGHT - mm*world->numCarnivore[q+1],0); - } - glColor3f(0.9,0.9,0); //frugivore count - for(int q=0;qptr-1) continue; - glVertex3f(-2010 + q*10,conf::HEIGHT - mm*world->numFrugivore[q],0); - glVertex3f(-2010 +(q+1)*10,conf::HEIGHT - mm*world->numFrugivore[q+1],0); - } - glColor3f(0,0,0); //total count - for(int q=0;qptr-1) continue; - glVertex3f(-2010 + q*10,conf::HEIGHT - mm*world->numTotal[q],0); - glVertex3f(-2010 +(q+1)*10,conf::HEIGHT - mm*world->numTotal[q+1],0); - } - glColor3f(0.8,0.8,0.6); //dead count - for(int q=0;qptr-1) continue; - glVertex3f(-2010 + q*10,conf::HEIGHT - mm*(world->numDead[q]+world->numTotal[q]),0); - glVertex3f(-2010 +(q+1)*10,conf::HEIGHT - mm*(world->numDead[q+1]+world->numTotal[q+1]),0); + glBegin(GL_LINES); + glColor3f(0,0,0.5); //hybrid count + glVertex3f(-UID::GRAPHWIDTH + q*xinterval, conf::HEIGHT - yscaling*world->numHybrid[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval, conf::HEIGHT - yscaling*world->numHybrid[q+1],0); + + glColor3f(0,1,0); //herbivore count + glVertex3f(-UID::GRAPHWIDTH + q*xinterval,conf::HEIGHT - yscaling*world->numHerbivore[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval,conf::HEIGHT - yscaling*world->numHerbivore[q+1],0); + + glColor3f(1,0,0); //carnivore count + glVertex3f(-UID::GRAPHWIDTH + q*xinterval,conf::HEIGHT - yscaling*world->numCarnivore[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval,conf::HEIGHT - yscaling*world->numCarnivore[q+1],0); + + glColor3f(0.9,0.9,0); //frugivore count + glVertex3f(-UID::GRAPHWIDTH + q*xinterval,conf::HEIGHT - yscaling*world->numFrugivore[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval,conf::HEIGHT - yscaling*world->numFrugivore[q+1],0); + + glColor3f(0,0,0); //total count + glVertex3f(-UID::GRAPHWIDTH + q*xinterval,conf::HEIGHT - yscaling*world->numTotal[q],0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval,conf::HEIGHT - yscaling*world->numTotal[q+1],0); + + glColor3f(0.8,0.8,0.6); //dead count + glVertex3f(-UID::GRAPHWIDTH + q*xinterval,conf::HEIGHT - yscaling*(world->numDead[q]+world->numTotal[q]),0); + glVertex3f(-UID::GRAPHWIDTH +(q+1)*xinterval,conf::HEIGHT - yscaling*(world->numDead[q+1]+world->numTotal[q+1]),0); + glEnd(); } //current population vertical bars - glVertex3f(-2010 + world->ptr*10,conf::HEIGHT,0); - glVertex3f(-2010 + world->ptr*10,conf::HEIGHT - mm*world->getAgents(),0); + glBegin(GL_LINES); + glVertex3f(0,conf::HEIGHT,0); + glVertex3f(0,conf::HEIGHT - yscaling*world->getAgents(),0); glColor3f(0,0,0); - glVertex3f(-2010 + world->ptr*10,conf::HEIGHT,0); - glVertex3f(-2010 + world->ptr*10,conf::HEIGHT - mm*world->getAlive(),0); + glVertex3f(0,conf::HEIGHT,0); + glVertex3f(0,conf::HEIGHT - yscaling*world->getAlive(),0); glEnd(); //labels for current population bars - int movetext= 0; - if (world->ptr>=conf::RECORD_SIZE*0.75) movetext= -700; + float ydeadlabel = yscaling*world->getAgents(); //since # of dead could go over the top of the graph, clamp it (don't worry about the bar) + float ymaxdeadlabel = conf::HEIGHT - (UID::CHARHEIGHT + 2)/scalemult; + if (ydeadlabel > ymaxdeadlabel) ydeadlabel = ymaxdeadlabel; + sprintf(buf2, "%i dead", world->getDead()); - RenderString(-2006 + movetext + world->ptr*10,conf::HEIGHT - mm*world->getAgents(), + RenderString((2 - ((float)UID::CHARWIDTH + 1.0f)*(float)strlen(buf2))/scalemult, conf::HEIGHT - ydeadlabel, GLUT_BITMAP_HELVETICA_12, buf2, 0.8f, 0.8f, 0.6f); + sprintf(buf2, "%i agents", world->getAlive()); - RenderString(-2006 + movetext + world->ptr*10,conf::HEIGHT - mm*world->getAlive(), + RenderString((8 - ((float)UID::CHARWIDTH + 1.0f)*(float)strlen(buf2))/scalemult, conf::HEIGHT - yscaling*world->getAlive(), GLUT_BITMAP_HELVETICA_12, buf2, 0.0f, 0.0f, 0.0f); + + glTranslatef(UID::GRAPHBUFFER, 0, 0); } if(getLayerDisplayCount()>0){ @@ -3123,12 +3322,12 @@ void GLView::drawData() glColor3f(0,0,0); //border around graphs and feedback glVertex3f(0,0,0); - glVertex3f(-2020,0,0); + glVertex3f(-UID::GRAPHWIDTH-UID::GRAPHBUFFER,0,0); - glVertex3f(-2020,0,0); - glVertex3f(-2020,conf::HEIGHT,0); + glVertex3f(-UID::GRAPHWIDTH-UID::GRAPHBUFFER,0,0); + glVertex3f(-UID::GRAPHWIDTH-UID::GRAPHBUFFER,conf::HEIGHT,0); - glVertex3f(-2020,conf::HEIGHT,0); + glVertex3f(-UID::GRAPHWIDTH-UID::GRAPHBUFFER,conf::HEIGHT,0); glVertex3f(0,conf::HEIGHT,0); glEnd(); } @@ -3149,8 +3348,7 @@ void GLView::drawStatic() gluOrtho2D(0, ww, 0, wh); // invert the y axis, down is positive glScalef(1, -1, 1); - // move the origin from the bottom left corner - // to the upper left corner + // move the origin from the bottom left corner to the upper left corner glTranslatef(0, -wh, 0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); @@ -3173,7 +3371,7 @@ void GLView::drawStatic() } } else if(line==StaticDisplay::FOLLOW) { if(live_follow!=0) { - if(world->getSelectedAgent()>=0) RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Following", 0.75f, 0.75f, 0.75f); + if(world->getSelectedAgentIndex()>=0) RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Following", 0.75f, 0.75f, 0.75f); else RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "No Follow Target", 0.75f, 0.75f, 0.75f); currentline++; } @@ -3210,10 +3408,10 @@ void GLView::drawStatic() } } else if(line==StaticDisplay::DROUGHT) { if(world->isDrought()) { - RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Drought Epoch", 1.0f, 1.0f, 0.0f); + RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Active Drought", 1.0f, 1.0f, 0.0f); currentline++; } else if(world->isOvergrowth()) { - RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Overgrowth Epoch", 0.0f, 0.65f, 0.25f); + RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Active Overgrowth", 0.0f, 0.65f, 0.25f); currentline++; } } else if (line==StaticDisplay::MUTATIONS) { @@ -3243,10 +3441,10 @@ void GLView::drawStatic() } } else if (line==StaticDisplay::CLIMATE) { if(world->isIceAge()) { - RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Ice Age Epoch", 0.3f, 0.9f, 0.9f); + RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Ice Age", 0.3f, 0.9f, 0.9f); currentline++; } else if(world->isHadean()) { - RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Hadean Epoch", 1.0f, 0.55f, 0.15f); + RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Hadean Climate", 1.0f, 0.55f, 0.15f); currentline++; } else if(world->isExtreme()) { RenderStringBlack(10, currentline*spaceperline, GLUT_BITMAP_HELVETICA_12, "Extreme Climate", 1.0f, 0.75f, 0.4f); @@ -3307,7 +3505,7 @@ void GLView::drawStatic() //event display y coord, based on Selected Agent Hud to avoid overlap - int euy= 20 + 2*UID::BUFFER+((int)ceil((float)SADHudOverview::HUDS/3))*(UID::CHARHEIGHT+UID::BUFFER); + int euy= 20 + 2*UID::BUFFER+((int)ceil((float)LADHudOverview::HUDS/3))*(UID::CHARHEIGHT+UID::BUFFER); //event log display float ss= 18; @@ -3358,10 +3556,10 @@ void GLView::drawStatic() } glBegin(GL_QUADS); - glColor4f(red,gre,blu,0.5*fade); + glColor4f(red,gre,blu,0.6*fade); glVertex3f(ww-UID::BUFFER-UID::EVENTSWIDTH, euy+5+(2+eo)*ss+toastbase+move,0); glVertex3f(ww-UID::BUFFER, euy+5+(2+eo)*ss+toastbase+move,0); - glColor4f(red*0.75,gre*0.75,blu*0.75,0.5*fade); + glColor4f(red*0.75,gre*0.75,blu*0.75,0.6*fade); glVertex3f(ww-UID::BUFFER, euy+5+(3+eo)*ss+toastbase+move,0); glVertex3f(ww-UID::BUFFER-UID::EVENTSWIDTH, euy+5+(3+eo)*ss+toastbase+move,0); glEnd(); @@ -3479,15 +3677,15 @@ void GLView::drawPieDisplay(float x, float y, float size, std::vectorred, color->gre, color->blu, 1); @@ -3633,21 +3831,20 @@ void GLView::renderAllTiles() //yes so we are importing an array from the so-called renderTile method because I wanted to minimize the number of times I set those //ugly? maybe. Bad code? I'll let you decide when you want to make a change to a single tile you're adding... - if(ti==MainTiles::SAD){ - //SAD: selected agent display + if(ti == MainTiles::LAD){ + //LAD: Loaded / seLected Agent Display Agent selected; Color3f *bg; - //or are we the LAD: Loaded Agent Display? If so, render a mock Loaded agent instead! - if(live_cursormode==1 && world->loadedagent.brain.boxes.size()==world->BRAINSIZE) { - selected= world->loadedagent; - selected.angle+= 2*sin((float)currentTime/200); //make loaded agent spin a little, and flash a selection icon + if(live_cursormode == MouseMode::PLACE_AGENT) { + selected = world->loadedagent; //load a mock agent + selected.angle += 2*sin((float)currentTime/200); //make loaded agent spin a little, and flash a selection icon if(world->modcounter%20==0) selected.initSplash(20, 1.0,1.0,1.0); - bg= new Color3f(0,0.45,0.1); //set a cool green background color + bg = new Color3f(0,0.45,0.1); //set a cool green background color } else { - //SAD defines - selected= world->agents[world->getSelectedAgent()]; - bg= new Color3f(0,0.4,0.6); //set background to our traditional blue + //LAD defines + selected = world->agents[world->getSelectedAgentIndex()]; //load exactly the same agent as the selected + bg = new Color3f(0,0.4,0.6); //set background to our traditional blue } //main box @@ -3695,54 +3892,57 @@ void GLView::renderAllTiles() glVertex3f(tbx,tby,0); glEnd(); + float centery = (float)(th)/2+3; + float centerx = tx+centery-13; - float centery= (float)(th)/2; - - if(live_cursormode==1){ + if(live_cursormode == MouseMode::PLACE_AGENT){ RenderString(tx+centery-35, ty2-15, GLUT_BITMAP_HELVETICA_12, "Place me!", 0.8f, 1.0f, 1.0f); - ui_sadmode= 1; //force below display mode if our SAD is actually a LAD + ui_ladmode = LADVisualMode::GHOST; //force ghost display mode if we're in PLACE_AGENT mode } - //next, depending on the current SAD visual mode, we render something in our extra space - if(ui_sadmode==1) { - //draw Ghost Agent - drawPreAgent(selected, tx+centery, 5+centery, true); - drawAgent(selected, tx+centery, 5+centery, true); - - } else if (ui_sadmode==2) { - //draw Damages pie chart - RenderString(tx+centery-45, ty+42, GLUT_BITMAP_HELVETICA_12, "Damage Taken:", 0.8f, 1.0f, 1.0f); - drawPieDisplay(tx+centery, 10+centery, 32, selected.damages); - } else if (ui_sadmode==3) { - //draw Intakes pie chart - RenderString(tx+centery-45, ty+42, GLUT_BITMAP_HELVETICA_12, "Food Intaken:", 0.8f, 1.0f, 1.0f); - drawPieDisplay(tx+centery, 10+centery, 32, selected.intakes); - } + //depending on the current LAD visual mode, we render something in our left-hand space + if(ui_ladmode == LADVisualMode::GHOST) { + drawPreAgent(selected, centerx, centery, true); + drawAgent(selected, centerx, centery, true); + + } else if (ui_ladmode == LADVisualMode::DAMAGES) { + RenderString(centerx-35, ty+40, GLUT_BITMAP_HELVETICA_12, "Damage Taken:", 0.8f, 1.0f, 1.0f); + drawPieDisplay(centerx, centery, 32, selected.damages); + + } else if (ui_ladmode == LADVisualMode::INTAKES) { + RenderString(centerx-35, ty+40, GLUT_BITMAP_HELVETICA_12, "Food Intaken:", 0.8f, 1.0f, 1.0f); + drawPieDisplay(centerx, centery, 32, selected.intakes); + } //note NONE is an option and renders nothing, leaving space for other things... //Agent ID glBegin(GL_QUADS); - glColor4f(0,0,0,0.7); float idlength= 19+UID::CHARWIDTH*floorf(1.0+log10((float)selected.id+1.0)); - float idboxx= tx+centery-idlength/2-5; + float idboxx= centerx-idlength/2-5; float idboxy= ty+11; - float idboxx2= tx+centery+idlength/2+5; + float idboxx2= centerx+idlength/2+10; float idboxy2= idboxy+14; - glVertex3f(idboxx,idboxy2,0); - glVertex3f(idboxx2,idboxy2,0); Color3f specie= setColorSpecies(selected.species); glColor4f(specie.red,specie.gre,specie.blu,1); - glVertex3f(idboxx2,idboxy,0); + glVertex3f(idboxx2,idboxy,0); glVertex3f(idboxx,idboxy,0); + glColor4f(specie.red/2,specie.gre/2,specie.blu/2,1); + glVertex3f(idboxx,idboxy2,0); + glVertex3f(idboxx2,idboxy2,0); glEnd(); sprintf(buf, "ID: %d", selected.id); - RenderString(tx+centery-idlength/2, idboxy2-3, GLUT_BITMAP_HELVETICA_12, buf, 0.8f, 1.0f, 1.0f); + RenderString(centerx-idlength/2, idboxy2-3, GLUT_BITMAP_HELVETICA_12, buf, 0.8f, 1.0f, 1.0f); + + //Show cause of death if dead + if(selected.isDead()){ + RenderString(tbx+UID::BUFFER-2, th-UID::TINYTILEWIDTH-UID::BUFFER-UID::TILEBEVEL*2, GLUT_BITMAP_HELVETICA_12, selected.death.c_str(), 0.8f, 1.0f, 1.0f); + } Color3f defaulttextcolor(0.8,1,1); Color3f traittextcolor(0.6,0.7,0.8); - for(int u=0; uEXHAUSTION_MULT*selected.exhaustion)); + } else if (u==LADHudOverview::EXHAUSTION){ //Exhaustion/energy indicator ux=ww-100, uy=25 + float exh= 2/(1+exp(selected.exhaustion)); glColor3f(0.8,0.8,0); glVertex3f(ux,uy,0); glVertex3f(ux,uy-UID::CHARHEIGHT,0); @@ -3810,36 +4010,52 @@ void GLView::renderAllTiles() glEnd(); } + //extra color tile for gene color + if(u==LADHudOverview::CHAMOVID) { + glBegin(GL_QUADS); + glColor4f(0,0,0,0.7); + glVertex3f(ux-2+UID::HUDSWIDTH-UID::CHARHEIGHT,uy+1,0); + glVertex3f(ux-2+UID::HUDSWIDTH-UID::CHARHEIGHT,uy-1-UID::CHARHEIGHT,0); + glVertex3f(ux+1+UID::HUDSWIDTH,uy-1-UID::CHARHEIGHT,0); + glVertex3f(ux+1+UID::HUDSWIDTH,uy+1,0); + + glColor3f(selected.gene_red,selected.gene_gre,selected.gene_blu); + glVertex3f(ux+UID::HUDSWIDTH-UID::CHARHEIGHT,uy,0); + glVertex3f(ux+UID::HUDSWIDTH-UID::CHARHEIGHT,uy-UID::CHARHEIGHT,0); + glVertex3f(ux+UID::HUDSWIDTH,uy-UID::CHARHEIGHT,0); + glVertex3f(ux+UID::HUDSWIDTH,uy,0); + glEnd(); + } //write text and values //VOLITILE TRAITS: - if(u==SADHudOverview::HEALTH){ + if(u==LADHudOverview::HEALTH){ if(live_agentsvis==Visual::HEALTH) drawbox= true; sprintf(buf, "Health: %.2f/%.0f", selected.health, conf::HEALTH_CAP); - } else if(u==SADHudOverview::REPCOUNTER){ + } else if(u==LADHudOverview::REPCOUNTER){ if(live_agentsvis==Visual::REPCOUNTER) drawbox= true; sprintf(buf, "Child: %.2f/%.0f", selected.repcounter, selected.maxrepcounter); - } else if(u==SADHudOverview::EXHAUSTION){ - if(world->EXHAUSTION_MULT>0 && selected.exhaustion>(5*world->EXHAUSTION_MULT)) sprintf(buf, "Exhausted!"); - else if (world->EXHAUSTION_MULT>0 && selected.exhaustion<(2*world->EXHAUSTION_MULT)) sprintf(buf, "Energetic!"); + } else if(u==LADHudOverview::EXHAUSTION){ + if(selected.exhaustion>5) sprintf(buf, "Exhausted!"); + else if (selected.exhaustion<2) sprintf(buf, "Energetic!"); else sprintf(buf, "Tired."); - } else if(u==SADHudOverview::AGE){ + } else if(u==LADHudOverview::AGE){ sprintf(buf, "Age: %.1f days", (float)selected.age/10); - } else if(u==SADHudOverview::GIVING){ + } else if(u==LADHudOverview::GIVING){ if(selected.isGiving()) sprintf(buf, "Generous"); else if(selected.give>conf::MAXSELFISH) sprintf(buf, "Autocentric"); else sprintf(buf, "Selfish"); -// } else if(u==SADHudOverview::DHEALTH){ +// } else if(u==LADHudOverview::DHEALTH){ // if(selected.dhealth==0) sprintf(buf, "Delta H: 0"); // else sprintf(buf, "Delta H: %.2f", selected.dhealth); - } else if(u==SADHudOverview::SPIKE){ - if(selected.health>0 && selected.isSpikey(world->SPIKELENGTH)){ + } else if(u==LADHudOverview::SPIKE){ + if(selected.health>0 && selected.isSpikey(world->SPIKE_LENGTH)){ float mw= selected.w1>selected.w2 ? selected.w1 : selected.w2; if(mw<0) mw= 0; float val= world->DAMAGE_FULLSPIKE*selected.spikeLength*mw; @@ -3848,58 +4064,58 @@ void GLView::renderAllTiles() else sprintf(buf, "Spike: ~%.2f h", val); } else sprintf(buf, "Not Spikey"); - } else if(u==SADHudOverview::SEXPROJECT){ + } else if(u==LADHudOverview::SEXPROJECT){ if(selected.isMale()) sprintf(buf, "Sexting (M)"); else if(selected.isAsexual()) sprintf(buf, "Is Asexual"); else sprintf(buf, "Sexting (F)"); - } else if(u==SADHudOverview::MOTION){ + } else if(u==LADHudOverview::MOTION){ if(selected.isAirborne()) sprintf(buf, "Airborne!"); else if(selected.encumbered) sprintf(buf, "Encumbered"); else if(selected.boost) sprintf(buf, "Boosting"); else if(abs(selected.w1-selected.w2)>0.06) sprintf(buf, "Spinning..."); - else if(abs(selected.w1)>0.1 || abs(selected.w2)>0.1) sprintf(buf, "Sprinting"); + else if(abs(selected.w1)>0.2 || abs(selected.w2)>0.2) sprintf(buf, "Sprinting"); else if(abs(selected.w1)>0.03 || abs(selected.w2)>0.03) sprintf(buf, "Moving"); - else if(selected.health<=0) sprintf(buf, "Dead."); + else if(selected.isDead()) sprintf(buf, "Dead."); else sprintf(buf, "Idle"); - } else if(u==SADHudOverview::GRAB){ + } else if(u==LADHudOverview::GRAB){ if(selected.isGrabbing()){ if(selected.grabID==-1) sprintf(buf, "Grabbing"); else sprintf(buf, "Hold %d (%.2f)", selected.grabID, selected.grabbing); } else sprintf(buf, "Not Grabbing"); - } else if(u==SADHudOverview::BITE){ + } else if(u==LADHudOverview::BITE){ if(selected.jawrend!=0) sprintf(buf, "Bite: %.2f h", abs(selected.jawPosition*world->DAMAGE_JAWSNAP)); else sprintf(buf, "Not Biting"); - } else if(u==SADHudOverview::WASTE){ + } else if(u==LADHudOverview::WASTE){ if(selected.out[Output::WASTE_RATE]<0.05) sprintf(buf, "Has the Runs"); else if(selected.out[Output::WASTE_RATE]<0.3) sprintf(buf, "Incontinent"); else if(selected.out[Output::WASTE_RATE]>0.7) sprintf(buf, "Waste Prudent"); else sprintf(buf, "Avg Waste Rate"); - } else if(u==SADHudOverview::VOLUME){ + } else if(u==LADHudOverview::VOLUME){ if(live_agentsvis==Visual::VOLUME) drawbox= true; if(selected.volume>0.8) sprintf(buf, "Loud! (%.3f)", selected.volume); else if(selected.volume<=conf::RENDER_MINVOLUME) sprintf(buf, "Silent."); else if(selected.volume<0.2) sprintf(buf, "Quiet (%.3f)", selected.volume); else sprintf(buf, "Volume: %.3f", selected.volume); - } else if(u==SADHudOverview::TONE){ + } else if(u==LADHudOverview::TONE){ sprintf(buf, "Tone: %.3f", selected.tone); //STATS: - } else if(u==SADHudOverview::STAT_CHILDREN){ + } else if(u==LADHudOverview::STAT_CHILDREN){ sprintf(buf, "Children: %d", selected.children); - } else if(u==SADHudOverview::STAT_KILLED){ + } else if(u==LADHudOverview::STAT_KILLED){ sprintf(buf, "Has Killed: %d", selected.killed); //FIXED TRAITS - } else if(u==SADHudOverview::STOMACH){ + } else if(u==LADHudOverview::STOMACH){ if(live_agentsvis==Visual::STOMACH) drawbox= true; if(selected.isHerbivore()) sprintf(buf, "\"Herbivore\""); else if(selected.isFrugivore()) sprintf(buf, "\"Frugivore\""); @@ -3907,11 +4123,12 @@ void GLView::renderAllTiles() else sprintf(buf, "\"???\"..."); isFixedTrait= true; - } else if(u==SADHudOverview::GENERATION){ + } else if(u==LADHudOverview::GENERATION){ + if(live_agentsvis==Visual::GENERATIONS) drawbox= true; sprintf(buf, "Gen: %d", selected.gencount); isFixedTrait= true; - } else if(u==SADHudOverview::TEMPPREF){ + } else if(u==LADHudOverview::TEMPPREF){ if(live_agentsvis==Visual::DISCOMFORT) drawbox= true; if(selected.temperature_preference>0.8) sprintf(buf, "Hadean(%.1f)", selected.temperature_preference*100); else if(selected.temperature_preference>0.6)sprintf(buf, "Tropical(%.1f)", selected.temperature_preference*100); @@ -3920,63 +4137,82 @@ void GLView::renderAllTiles() else sprintf(buf, "Temperate(%.1f)", selected.temperature_preference*100); isFixedTrait= true; - } else if(u==SADHudOverview::LUNGS){ + } else if(u==LADHudOverview::LUNGS){ if(live_agentsvis==Visual::LUNGS) drawbox= true; if(selected.isAquatic()) sprintf(buf, "Aquatic(%.3f)", selected.lungs); else if (selected.isTerrestrial()) sprintf(buf, "Terran(%.3f)", selected.lungs); else sprintf(buf, "Amphibian(%.2f)", selected.lungs); isFixedTrait= true; - } else if(u==SADHudOverview::SIZE){ + } else if(u==LADHudOverview::SIZE){ sprintf(buf, "Radius: %.2f", selected.radius); isFixedTrait= true; - } else if(u==SADHudOverview::MUTCHANCE){ - sprintf(buf, "Mut-rate: %.3f", selected.MUTCHANCE); + } else if(u==LADHudOverview::BRAINMUTCHANCE){ + if(live_agentsvis==Visual::BRAINMUTATION) drawbox= true; + sprintf(buf, "Brain ~%%: %.3f", selected.brain_mutation_chance); + isFixedTrait= true; + } else if(u==LADHudOverview::BRAINMUTSIZE){ + if(live_agentsvis==Visual::BRAINMUTATION) drawbox= true; + sprintf(buf, "Brain ~Sz: %.3f", selected.brain_mutation_size); isFixedTrait= true; - } else if(u==SADHudOverview::MUTSIZE){ - sprintf(buf, "Mut-size: %.3f", selected.MUTSIZE); + } else if(u==LADHudOverview::GENEMUTCHANCE){ + if(live_agentsvis==Visual::GENEMUTATION) drawbox= true; + sprintf(buf, "Gene ~%%: %.3f", selected.gene_mutation_chance); + isFixedTrait= true; + } else if(u==LADHudOverview::GENEMUTSIZE){ + if(live_agentsvis==Visual::GENEMUTATION) drawbox= true; + sprintf(buf, "Gene ~Sz: %.3f", selected.gene_mutation_size); isFixedTrait= true; - } else if(u==SADHudOverview::CHAMOVID){ + } else if(u==LADHudOverview::CHAMOVID){ if(live_agentsvis==Visual::RGB) drawbox= true; sprintf(buf, "Camo: %.3f", selected.chamovid); isFixedTrait= true; - } else if(u==SADHudOverview::METABOLISM){ + } else if(u==LADHudOverview::METABOLISM){ if(live_agentsvis==Visual::METABOLISM) drawbox= true; - sprintf(buf, "Metab: %.2f", selected.metabolism); + if(selected.metabolism>0.9) sprintf(buf, "Metab:%.2f ++C", selected.metabolism); + else if(selected.metabolism>0.6) sprintf(buf, "Metab: %.2f +C", selected.metabolism); + else if(selected.metabolism<0.1) sprintf(buf, "Metab:%.2f ++H", selected.metabolism); + else if(selected.metabolism<0.4) sprintf(buf, "Metab: %.2f +H", selected.metabolism); + else sprintf(buf, "Metab: %.2f B", selected.metabolism); isFixedTrait= true; - } else if(u==SADHudOverview::HYBRID){ + } else if(u==LADHudOverview::HYBRID){ if(selected.hybrid) sprintf(buf, "Is Hybrid"); else if(selected.gencount==0) sprintf(buf, "Was Spawned"); else sprintf(buf, "Was Budded"); isFixedTrait= true; - } else if(u==SADHudOverview::STRENGTH){ + } else if(u==LADHudOverview::STRENGTH){ + if(live_agentsvis==Visual::STRENGTH) drawbox= true; if(selected.strength>0.7) sprintf(buf, "Strong!"); else if(selected.strength>0.3) sprintf(buf, "Not Weak"); else sprintf(buf, "Weak!"); isFixedTrait= true; - } else if(u==SADHudOverview::NUMBABIES){ + } else if(u==LADHudOverview::NUMBABIES){ sprintf(buf, "Num Babies: %d", selected.numbabies); isFixedTrait= true; - } else if(u==SADHudOverview::SPECIESID){ + } else if(u==LADHudOverview::SPECIESID){ if(live_agentsvis==Visual::SPECIES) drawbox= true; sprintf(buf, "Gene: %d", selected.species); isFixedTrait= true; - } else if(u==SADHudOverview::KINRANGE){ + } else if(u==LADHudOverview::KINRANGE){ if(live_agentsvis==Visual::CROSSABLE) drawbox= true; sprintf(buf, "Kin Range: %d", selected.kinrange); isFixedTrait= true; - } else if(u==SADHudOverview::DEATH && selected.health==0){ - //technically is an unchanging stat, but every agent only ever gets just one, so let's keep it bright - sprintf(buf, selected.death.c_str()); + } else if(u==LADHudOverview::BRAINSIZE){ + sprintf(buf, "Conns: %d", selected.brain.conns.size()); + //isFixedTrait= true; //technically is an unchanging stat, but it's like, really important + +// } else if(u==LADHudOverview::DEATH && selected.isDead()){ +// sprintf(buf, selected.death.c_str()); + //isFixedTrait= true; //technically is an unchanging stat, but every agent only ever gets just one, so let's keep it bright } else sprintf(buf, ""); @@ -4028,43 +4264,46 @@ void GLView::drawCell(int x, int y, const float values[Layer::LAYERS]) Color3f cellcolor(0.0, 0.0, 0.0); //init color additives - Color3f light= live_layersvis[DisplayLayer::LIGHT] ? setColorLight(values[Layer::LIGHT]) : setColorLight(1); + Color3f light= live_layersvis[DisplayLayer::LIGHT] ? setColorLight(values[Layer::LIGHT]) : Color3f(1); - Color3f terrain= live_layersvis[DisplayLayer::ELEVATION] ? setColorHeight(values[Layer::ELEVATION]) : setColorHeight(0); + Color3f terrain= live_layersvis[DisplayLayer::ELEVATION] ? setColorHeight(values[Layer::ELEVATION]) : Color3f(0); - Color3f plant= live_layersvis[DisplayLayer::PLANTS] ? setColorPlant(values[Layer::PLANTS]) : setColorHeight(0); + Color3f plant= live_layersvis[DisplayLayer::PLANTS] ? setColorPlant(values[Layer::PLANTS]) : Color3f(0); - Color3f hazard= live_layersvis[DisplayLayer::HAZARDS] ? setColorHazard(values[Layer::HAZARDS]) : setColorHeight(0); + Color3f hazard= live_layersvis[DisplayLayer::HAZARDS] ? setColorHazard(values[Layer::HAZARDS]) : Color3f(0); - Color3f fruit= live_layersvis[DisplayLayer::FRUITS] ? setColorFruit(values[Layer::FRUITS]) : setColorHeight(0); + Color3f fruit= live_layersvis[DisplayLayer::FRUITS] ? setColorFruit(values[Layer::FRUITS]) : Color3f(0); - Color3f meat= live_layersvis[DisplayLayer::MEATS] ? setColorMeat(values[Layer::MEATS]) : setColorHeight(0); + Color3f meat= live_layersvis[DisplayLayer::MEATS] ? setColorMeat(values[Layer::MEATS]) : Color3f(0); - Color3f temp= live_layersvis[DisplayLayer::TEMP] ? setColorTempCell(y) : setColorHeight(0); //special for temp: until cell-based, convert y coord and process + Color3f temp= live_layersvis[DisplayLayer::TEMP] ? setColorTempCell(y) : Color3f(0); //special for temp: until cell-based, convert y coord and process if(layers>1) { //if more than one layer selected, some layers display VERY differently //set terrain to use terrain instead of just elevation - terrain= live_layersvis[DisplayLayer::ELEVATION] ? setColorTerrain(values[Layer::ELEVATION]) : setColorHeight(0); + terrain= live_layersvis[DisplayLayer::ELEVATION] ? setColorTerrain(values[Layer::ELEVATION]) : Color3f(0); - //stop fruit from displaying if we're also displaying plants or elevation - if(live_layersvis[DisplayLayer::PLANTS] || live_layersvis[DisplayLayer::ELEVATION]){ - fruit= setColorHeight(0); + //stop fruit from displaying if we're also displaying plants + if(live_layersvis[DisplayLayer::PLANTS]){ + fruit= Color3f(0); + // live_layersvis[DisplayLayer::ELEVATION] //stop displaying meat if we're also displaying plants, fruit, or elevation - if(live_layersvis[DisplayLayer::FRUITS]) meat= setColorHeight(0); + if(live_layersvis[DisplayLayer::FRUITS]) meat= Color3f(0); } - //if only disp light and another layer, mix them lightly. if(layers==2) { + //if only disp light and another layer (except elevation), mix them lightly. if(live_layersvis[DisplayLayer::LIGHT] && !live_layersvis[DisplayLayer::ELEVATION]) { - terrain= setColorHeight(0.5); // this allows "light" to be "seen" + terrain= Color3f(0.5); // this allows "light" to be "seen" } + //if displaying temp and another layer, mix with temp, and dim elevation too if it's the other one if(live_layersvis[DisplayLayer::TEMP]) { temp.red*= 0.45; temp.gre*= 0.45; temp.blu*= 0.45; if(live_layersvis[DisplayLayer::ELEVATION]) terrain= setColorHeight(values[Layer::ELEVATION]/4+0.25); } } else { + //if more than 2 layers displayed, always dim temp temp.red*= 0.15; temp.gre*= 0.15; temp.blu*= 0.15; } @@ -4075,7 +4314,7 @@ void GLView::drawCell(int x, int y, const float values[Layer::LAYERS]) hazard.red*= 0.85; hazard.gre*= 0.85; hazard.blu*= 0.85; } - } else if(live_layersvis[DisplayLayer::LIGHT]) terrain= setColorHeight(1); //if only light layer, set terrain to 1 so we show something + } else if(live_layersvis[DisplayLayer::LIGHT]) terrain= Color3f(1); //if only light layer, set terrain to 1 so we show something //If we're displaying terrain... if(live_layersvis[DisplayLayer::ELEVATION]){ @@ -4086,18 +4325,18 @@ void GLView::drawCell(int x, int y, const float values[Layer::LAYERS]) } else { //...and cell is water... //...then change plant and hazard colors in water - plant= live_layersvis[DisplayLayer::PLANTS] ? setColorWaterPlant(values[Layer::PLANTS]) : setColorHeight(0); - hazard= live_layersvis[DisplayLayer::HAZARDS] ? setColorWaterHazard(values[Layer::HAZARDS]) : setColorHeight(0); + plant= live_layersvis[DisplayLayer::PLANTS] ? setColorWaterPlant(values[Layer::PLANTS]) : Color3f(0); + hazard= live_layersvis[DisplayLayer::HAZARDS] ? setColorWaterHazard(values[Layer::HAZARDS]) : Color3f(0); } - //who would have believed that the best place for audio playing based on visible terrain would have been within the cell-drawing code?... - if(scalemult>0.3 && x%8==0 && y%8==0 && (world->modcounter+x*53+y*19)%600==400) { - if(values[Layer::ELEVATION]<=Elevation::BEACH_MID*0.1) - world->tryPlayAudio(conf::SFX_BEACH1, 0, 0, 1.0, cap(scalemult-0.3)); - //else world->tryPlayAudio(conf::SFX_BEACH1, 0.5+x, 0.5+y, 1.0, cap(scalemult)); - } + //need to move this to Control.cpp and make better use of audio streams +// if(scalemult>0.3 && x%8==0 && y%8==0 && (world->modcounter+x*53+y*19)%600==400) { +// if(values[Layer::ELEVATION]<=Elevation::BEACH_MID*0.1) +// world->tryPlayAudio(conf::SFX_BEACH1, 0, 0, 1.0, cap(scalemult-0.3)); +// //else world->tryPlayAudio(conf::SFX_BEACH1, 0.5+x, 0.5+y, 1.0, cap(scalemult)); +// } } //If we're displaying hazards and there's a hazard event, render it @@ -4176,6 +4415,19 @@ void GLView::drawCell(int x, int y, const float values[Layer::LAYERS]) } } + if(x==(int)floorf(conf::WIDTH/conf::CZ)-1) { + //render a "themometer" on the right side, if enabled + temp= setColorTempCell(y); + int margin= 1; + int width= 2; + + glColor4f(temp.red, temp.gre, temp.blu, 1); + glVertex3f(conf::WIDTH+margin*conf::CZ, y*conf::CZ,0); + glVertex3f(conf::WIDTH+margin*conf::CZ+conf::CZ*width, y*conf::CZ,0); + glVertex3f(conf::WIDTH+margin*conf::CZ+conf::CZ*width, y*conf::CZ+conf::CZ,0); + glVertex3f(conf::WIDTH+margin*conf::CZ, y*conf::CZ+conf::CZ,0); + } + glEnd(); } } diff --git a/SOURCE/GLView.h b/SOURCE/GLView.h index e812eec..580611c 100644 --- a/SOURCE/GLView.h +++ b/SOURCE/GLView.h @@ -35,6 +35,7 @@ void glui_handleCloses(int action); struct Color3f { Color3f() : red(0), gre(0), blu(0.1) {} //navy-blue default color + Color3f(float v) : red(v), gre(v), blu(v) {} Color3f(float r, float g) : red(r), gre(g), blu(0.1) {} Color3f(float r, float g, float b) : red(r), gre(g), blu(b) {} void mix(Color3f ocolor) { @@ -113,9 +114,14 @@ class GLView Color3f setColorSpecies(float species); Color3f setColorCrossable(float species); Color3f setColorGenerocity(float give); - Color3f setColorRepCount(float repcount, bool asexual); - Color3f setColorMutations(float rate, float size); Color3f setColorStrength(float strength); + Color3f setColorRepType(int type); + Color3f setColorRepCount(float repcount, int type); + Color3f setColorMutations(float rate, float size); + Color3f setColorGeneration(int gen); + + //3f agent part color defs + std::pair setColorEar(int index); //3f cell color defs Color3f setColorCellsAll(const float values[Layer::LAYERS]); @@ -152,10 +158,12 @@ class GLView World *world; //the WORLD void syncLiveWithWorld(); //sync all important variables with their World counterparts, because they could have been changed by world //live variable support via glui + int live_demomode; //are we in demo mode? int live_mousemode; //what mode is the mouse using? int live_worldclosed; //is the world closed? int live_paused; //are we paused? int live_playmusic; //is music allowed to play? + int live_playsounds; //are any sounds allowed to play at all? int live_fastmode; //are we drawing? int live_skipdraw; //are we skipping some frames? int live_agentsvis; //are we drawing agents? If so, what's the scheme? see namespace "Visuals" in settings.h for details @@ -167,6 +175,7 @@ class GLView int live_autosave; //are we allowing autosaves? int live_grid; //override usual grid behavior and always draw grid? int live_hidedead; //hide dead agents? + int live_hidegenz; //hide generation zero agents? (no hard feelings) int live_landspawns; // are landspawns enabled int live_moonlight; //is moonlight enabled? float live_oceanpercent; //what is the setting for the percentage of water in the world? @@ -217,7 +226,7 @@ class GLView bool uiclicked; //was the ui clicked recently? used to disable drag fuctions if inital click was on UI int ui_layerpreset; //user can select a preset of layer displays using this - int ui_sadmode; //what rendering mode are we using for the Selected Agent Display? 0= off (text only), 1= normal agent view, 2= damage pie chart + int ui_ladmode; //what rendering mode are we using for the Loaded Agent Display? bool ui_movetiles; //are we allowing tiles to be moved? std::vector maintiles; //list of interactive tile buttons! WIP diff --git a/SOURCE/ReadWrite.cpp b/SOURCE/ReadWrite.cpp index 945e9cc..d079903 100644 --- a/SOURCE/ReadWrite.cpp +++ b/SOURCE/ReadWrite.cpp @@ -28,11 +28,6 @@ #include "ReadWrite.h" -#include "settings.h" -#include "helpers.h" -#include -#include - using namespace std; ReadWrite::ReadWrite() @@ -70,33 +65,49 @@ void ReadWrite::loadSettings(const char *filename) void ReadWrite::saveAgent(Agent *a, FILE *file) { //NOTE: this method REQUIRES "file" to be opened and closed outside + //flags: = agent, = eye, = ear, =brain, = conn, = box fprintf(file, "\n"); //signals the writing of a new agent -// fprintf(file, "id= %i\n", a->id); //id not loaded + //variables: changing values fprintf(file, "posx= %f\nposy= %f\n", a->pos.x, a->pos.y); fprintf(file, "angle= %f\n", a->angle); + fprintf(file, "age= %i\n", a->age); + fprintf(file, "gen= %i\n", a->gencount); + fprintf(file, "hybrid= %i\n", (int) a->hybrid); fprintf(file, "health= %f\n", a->health); + fprintf(file, "exhaustion= %f\n", a->exhaustion); + fprintf(file, "repcounter= %f\n", a->repcounter); + fprintf(file, "carcasscount= %i\n", a->carcasscount); + fprintf(file, "freshkill= %i\n", a->freshkill); + fprintf(file, "spike= %f\n", a->spikeLength); + fprintf(file, "jump= %f\n", a->jump); + fprintf(file, "dhealth= %f\n", a->dhealth); + + //traits: fixed values + fprintf(file, "species= %i\n", a->species); + fprintf(file, "kinrange= %i\n", a->kinrange); + fprintf(file, "radius= %f\n", a->radius); fprintf(file, "gene_red= %f\ngene_gre= %f\ngene_blu= %f\n", a->gene_red, a->gene_gre, a->gene_blu); fprintf(file, "chamovid= %f\n", a->chamovid); fprintf(file, "sexprojectbias= %f\n", a->sexprojectbias); fprintf(file, "herb= %f\n", a->stomach[Stomach::PLANT]); fprintf(file, "carn= %f\n", a->stomach[Stomach::MEAT]); fprintf(file, "frug= %f\n", a->stomach[Stomach::FRUIT]); - fprintf(file, "exhaustion= %f\n", a->exhaustion); - fprintf(file, "carcasscount= %i\n", a->carcasscount); - fprintf(file, "species= %i\n", a->species); - fprintf(file, "kinrange= %i\n", a->kinrange); - fprintf(file, "radius= %f\n", a->radius); - fprintf(file, "spike= %f\n", a->spikeLength); - fprintf(file, "jump= %f\n", a->jump); - fprintf(file, "dhealth= %f\n", a->dhealth); - fprintf(file, "age= %i\n", a->age); - fprintf(file, "gen= %i\n", a->gencount); - fprintf(file, "hybrid= %i\n", (int) a->hybrid); + fprintf(file, "numbabies= %i\n", a->numbabies); + fprintf(file, "metab= %f\n", a->metabolism); + fprintf(file, "temppref= %f\n", a->temperature_preference); + fprintf(file, "lungs= %f\n", a->lungs); + fprintf(file, "brain_mutchance= %f\n", a->brain_mutation_chance); + fprintf(file, "brain_mutsize= %f\n", a->brain_mutation_size); + fprintf(file, "gene_mutchance= %f\n", a->gene_mutation_chance); + fprintf(file, "gene_mutsize= %f\n", a->gene_mutation_size); + fprintf(file, "parentid= %i\n", a->parentid); + fprintf(file, "strength= %f\n", a->strength); fprintf(file, "cl1= %f\ncl2= %f\n", a->clockf1, a->clockf2); fprintf(file, "smellmod= %f\n", a->smell_mod); fprintf(file, "hearmod= %f\n", a->hear_mod); fprintf(file, "bloodmod= %f\n", a->blood_mod); - fprintf(file, "eyemod= %f\n", a->eye_see_agent_mod); + fprintf(file, "eyemod_agent= %f\n", a->eye_see_agent_mod); + fprintf(file, "eyemod_cell= %f\n", a->eye_see_cell_mod); // fprintf(file, "eyecellmod= %f\n", a->eye_see_cell_mod); for(int q=0;qeyedir.size();q++) { fprintf(file, "\n"); @@ -113,37 +124,36 @@ void ReadWrite::saveAgent(Agent *a, FILE *file) fprintf(file, "hearhigh= %f\n", a->hearhigh[q]); fprintf(file, "\n"); } - fprintf(file, "numbabies= %i\n", a->numbabies); - fprintf(file, "metab= %f\n", a->metabolism); - fprintf(file, "repc= %f\n", a->repcounter); - fprintf(file, "temppref= %f\n", a->temperature_preference); - fprintf(file, "lungs= %f\n", a->lungs); + //Note: Stats aren't saved // fprintf(file, "indicator= %f\n", a->indicator); // fprintf(file, "ir= %f\nig= %f\nib= %f\n", a->ir, a->ig, a->ib); -// fprintf(file, "give= %f\n", a->give); - fprintf(file, "mutchance= %f\nmutsize= %f\n", a->MUTCHANCE, a->MUTSIZE); - fprintf(file, "parentid= %i\n", a->parentid); - fprintf(file, "freshkill= %i\n", a->freshkill); - fprintf(file, "strength= %f\n", a->strength); fprintf(file, "\n"); //signals the writing of the brain (more for organization than proper loading) + fprintf(file, "boxes= %i\n", a->brain.boxes.size()); + fprintf(file, "conns= %i\n", a->brain.conns.size()); + + for(int c=0;cbrain.conns.size();c++){ + fprintf(file, "\n"); //signals the writing of a specific numbered conn + fprintf(file, "conn#= %i\n", c); + fprintf(file, "type= %i\n", a->brain.conns[c].type); + fprintf(file, "w= %f\n", a->brain.conns[c].w); + fprintf(file, "sid= %i\n", a->brain.conns[c].sid); + fprintf(file, "tid= %i\n", a->brain.conns[c].tid); + fprintf(file, "seed= %i\n", a->brain.conns[c].seed); + fprintf(file, "\n"); //end of conn + } for(int b=0;bbrain.boxes.size();b++){ fprintf(file, "\n"); //signals the writing of a specific numbered box fprintf(file, "box#= %i\n", b); fprintf(file, "kp= %f\n", a->brain.boxes[b].kp); + fprintf(file, "globalw= %f\n", a->brain.boxes[b].gw); fprintf(file, "bias= %f\n", a->brain.boxes[b].bias); fprintf(file, "seed= %i\n", a->brain.boxes[b].seed); - fprintf(file, "globalw= %f\n", a->brain.boxes[b].gw); - fprintf(file, "target= %f\n", a->brain.boxes[b].target); - fprintf(file, "out= %f\n", a->brain.boxes[b].out); - fprintf(file, "oldout= %f\n", a->brain.boxes[b].oldout); - for(int c=0;c\n"); //signals the writing of a specific connection for a specific box - fprintf(file, "conn#= %i\n", c); - fprintf(file, "type= %i\n", a->brain.boxes[b].type[c]); - fprintf(file, "w= %f\n", a->brain.boxes[b].w[c]); - fprintf(file, "cid= %i\n", a->brain.boxes[b].id[c]); - fprintf(file, "\n"); //end of connection + fprintf(file, "dead= %i\n", (int)a->brain.boxes[b].dead); + if(!a->isDead()) { + fprintf(file, "target= %f\n", a->brain.boxes[b].target); + fprintf(file, "out= %f\n", a->brain.boxes[b].out); + fprintf(file, "oldout= %f\n", a->brain.boxes[b].oldout); } fprintf(file, "\n"); //end of box } @@ -157,14 +167,25 @@ void ReadWrite::loadAgents(World *world, FILE *file, float fileversion, bool loa //loadexact is flag for loading agent EXACTLY is same pos, same health, exhaustion, etc, if set to false we give it random spawn settings for non-genes char address[32]; char line[64], *pos; - char var[16]; + char var[32]; char dataval[16]; - int mode= 2;//loading mode: -1= off, 0= world, 1= cell, 2= agent, 3= box, 4= connection, 5= eyes, 6= ears - - Agent xa(world->BRAINSIZE, world->MEANRADIUS, world->REP_PER_BABY, world->DEFAULT_MUTCHANCE, world->DEFAULT_MUTSIZE); //mock agent. gets moved and deleted after loading + int mode= ReadWriteMode::READY; + + Agent xa( + world->BRAINBOXES, + world->BRAINCONNS, + world->SPAWN_MIRROR_EYES, + world->OVERRIDE_KINRANGE, + world->MEANRADIUS, + world->REP_PER_BABY, + world->DEFAULT_BRAIN_MUTCHANCE, + world->DEFAULT_BRAIN_MUTSIZE, + world->DEFAULT_GENE_MUTCHANCE, + world->DEFAULT_GENE_MUTSIZE + ); //Agent::Agent //mock agent. gets moved and deleted after loading bool t2= false; //triggers for keeping track of where exactly we are - int eyenum= -1; //counters + int eyenum= -1; //temporary index holders int earnum= -1; int boxnum= -1; int connnum= -1; @@ -176,22 +197,26 @@ void ReadWrite::loadAgents(World *world, FILE *file, float fileversion, bool loa fgets(line, sizeof(line), file); pos= strtok(line,"\n"); sscanf(line, "%s%s", var, dataval); + + if(mode==ReadWriteMode::READY) { + if(strcmp(var, "")==0){ + mode = ReadWriteMode::AGENT; + } + } - if(mode==2){ + if(mode==ReadWriteMode::AGENT){ if(strcmp(var, "")==0){ //end agent tag is checked for, and when found, copies agent xa to the world - //first, check a few compatability issues for old saves: - if(fileversion<0.065) xa.kinrange= conf::MAXDEVIATION; - if(loadexact) world->addAgent(xa); else world->loadedagent= xa; //if we are loading a single agent, push it to buffer - }else if(strcmp(var, "posx=")==0 && loadexact){ sscanf(dataval, "%f", &f); xa.pos.x= f; + xa.dpos.x= f; }else if(strcmp(var, "posy=")==0 && loadexact){ sscanf(dataval, "%f", &f); xa.pos.y= f; + xa.dpos.y= f; }else if(strcmp(var, "angle=")==0 && loadexact){ sscanf(dataval, "%f", &f); xa.angle= f; @@ -271,34 +296,39 @@ void ReadWrite::loadAgents(World *world, FILE *file, float fileversion, bool loa }else if(strcmp(var, "bloodmod=")==0){ sscanf(dataval, "%f", &f); xa.blood_mod= f; - }else if(strcmp(var, "eyemod=")==0){ + }else if(strcmp(var, "eyemod_agent=")==0){ sscanf(dataval, "%f", &f); xa.eye_see_agent_mod= f; -// }else if(strcmp(var, "eyecellmod=")==0){ -// sscanf(dataval, "%f", &f); -// xa.eye_see_cell_mod= f; + }else if(strcmp(var, "eyemod_cell=")==0){ + sscanf(dataval, "%f", &f); + xa.eye_see_cell_mod= f; }else if(strcmp(var, "numbabies=")==0){ sscanf(dataval, "%i", &i); xa.numbabies= i; }else if(strcmp(var, "metab=")==0){ sscanf(dataval, "%f", &f); xa.metabolism= f; - }else if(strcmp(var, "repc=")==0){ + }else if(strcmp(var, "repcounter=")==0){ sscanf(dataval, "%f", &f); xa.repcounter= f; }else if(strcmp(var, "temppref=")==0){ sscanf(dataval, "%f", &f); xa.temperature_preference= f; - if(fileversion<0.055) xa.temperature_preference= 1-f; //invert temp preference number from version 0.05 or before }else if(strcmp(var, "lungs=")==0){ sscanf(dataval, "%f", &f); xa.lungs= f; - }else if(strcmp(var, "mutchance=")==0 || strcmp(var, "mutrate1=")==0){ + }else if(strcmp(var, "brain_mutchance=")==0){ + sscanf(dataval, "%f", &f); + xa.brain_mutation_chance= f; + }else if(strcmp(var, "brain_mutsize=")==0){ + sscanf(dataval, "%f", &f); + xa.brain_mutation_size= f; + }else if(strcmp(var, "gene_mutchance=")==0){ sscanf(dataval, "%f", &f); - xa.MUTCHANCE= f; - }else if(strcmp(var, "mutsize=")==0 || strcmp(var, "mutrate2=")==0){ + xa.gene_mutation_chance= f; + }else if(strcmp(var, "gene_mutsize=")==0){ sscanf(dataval, "%f", &f); - xa.MUTSIZE= f; + xa.gene_mutation_size= f; }else if(strcmp(var, "parentid=")==0){ sscanf(dataval, "%i", &i); xa.parentid= i; @@ -308,16 +338,24 @@ void ReadWrite::loadAgents(World *world, FILE *file, float fileversion, bool loa }else if(strcmp(var, "strength=")==0){ sscanf(dataval, "%f", &f); xa.strength= f; +// }else if(strcmp(var, "boxes=")==0){ +// sscanf(dataval, "%i", &i); +// xa.brain.boxes.resize(i); + }else if(strcmp(var, "conns=")==0){ + sscanf(dataval, "%i", &i); + xa.brain.conns.resize(i); }else if(strcmp(var, "")==0){ - mode= 5; //eye mode + mode= ReadWriteMode::EYE; //eye mode + }else if(strcmp(var, "")==0){ + mode= ReadWriteMode::CONN; //brain connection mode }else if(strcmp(var, "")==0){ - mode= 3; //brain mode + mode= ReadWriteMode::BOX; //brain box mode }else if(strcmp(var, "")==0){ - mode= 6; //ear mode + mode= ReadWriteMode::EAR; //ear mode } - }else if(mode==5){ //mode @ 5 = eye (of agent) + }else if(mode==ReadWriteMode::EYE){ if(strcmp(var, "")==0){ - mode= 2; + mode= ReadWriteMode::AGENT; //revert to agent mode }else if(strcmp(var, "eye#=")==0){ sscanf(dataval, "%i", &i); eyenum= i; @@ -328,9 +366,9 @@ void ReadWrite::loadAgents(World *world, FILE *file, float fileversion, bool loa sscanf(dataval, "%f", &f); xa.eyefov[eyenum]= f; } - }else if(mode==6){ //mode @ 6 = ear (of agent) + }else if(mode==ReadWriteMode::EAR){ if(strcmp(var, "")==0){ - mode= 2; + mode= ReadWriteMode::AGENT; //revert to agent mode }else if(strcmp(var, "ear#=")==0){ sscanf(dataval, "%i", &i); earnum= i; @@ -344,24 +382,28 @@ void ReadWrite::loadAgents(World *world, FILE *file, float fileversion, bool loa sscanf(dataval, "%f", &f); xa.hearhigh[earnum]= f; } - }else if(mode==3){ //mode @ 3 = brain box (of agent) + }else if(mode==ReadWriteMode::BOX){ if(strcmp(var, "")==0){ - mode= 2; + mode= ReadWriteMode::AGENT; //revert to agent mode }else if(strcmp(var, "box#=")==0){ sscanf(dataval, "%i", &i); boxnum= i; - }else if(strcmp(var, "seed=")==0){ - sscanf(dataval, "%i", &i); - xa.brain.boxes[boxnum].seed= i; }else if(strcmp(var, "kp=")==0){ sscanf(dataval, "%f", &f); xa.brain.boxes[boxnum].kp= f; - }else if(strcmp(var, "bias=")==0){ - sscanf(dataval, "%f", &f); - xa.brain.boxes[boxnum].bias= f; }else if(strcmp(var, "globalw=")==0){ sscanf(dataval, "%f", &f); xa.brain.boxes[boxnum].gw= f; + }else if(strcmp(var, "bias=")==0){ + sscanf(dataval, "%f", &f); + xa.brain.boxes[boxnum].bias= f; + }else if(strcmp(var, "seed=")==0){ + sscanf(dataval, "%i", &i); + xa.brain.boxes[boxnum].seed= i; + }else if(strcmp(var, "dead=")==0){ + sscanf(dataval, "%i", &i); + if(i>=1) xa.brain.boxes[boxnum].dead= true; + else xa.brain.boxes[boxnum].dead= false; }else if(strcmp(var, "target=")==0){ sscanf(dataval, "%f", &f); xa.brain.boxes[boxnum].target= f; @@ -371,24 +413,28 @@ void ReadWrite::loadAgents(World *world, FILE *file, float fileversion, bool loa }else if(strcmp(var, "oldout=")==0){ sscanf(dataval, "%f", &f); xa.brain.boxes[boxnum].oldout= f; - }else if(strcmp(var, "")==0){ - mode= 4; } - }else if(mode==4){ //mode @ 4 = connection (of brain box of agent) + }else if(mode==ReadWriteMode::CONN){ if(strcmp(var, "")==0){ - mode= 3; + mode= ReadWriteMode::AGENT; //revert to agent mode }else if(strcmp(var, "conn#=")==0){ sscanf(dataval, "%i", &i); connnum= i; }else if(strcmp(var, "type=")==0){ sscanf(dataval, "%i", &i); - xa.brain.boxes[boxnum].type[connnum]= i; + xa.brain.conns[connnum].type = i; }else if(strcmp(var, "w=")==0){ sscanf(dataval, "%f", &f); - xa.brain.boxes[boxnum].w[connnum]= f; - }else if(strcmp(var, "cid=")==0){ - sscanf(dataval, "%f", &f); - xa.brain.boxes[boxnum].id[connnum]= f; + xa.brain.conns[connnum].w = f; + }else if(strcmp(var, "sid=")==0){ + sscanf(dataval, "%i", &i); + xa.brain.conns[connnum].sid = i; + }else if(strcmp(var, "tid=")==0){ + sscanf(dataval, "%i", &i); + xa.brain.conns[connnum].tid = i; + }else if(strcmp(var, "seed=")==0){ + sscanf(dataval, "%i", &i); + xa.brain.conns[connnum].seed = i; } } } @@ -410,7 +456,7 @@ void ReadWrite::loadAgentFile(World *world, const char *address) } -void ReadWrite::saveWorld(World *world, float xpos, float ypos, const char *filename) +void ReadWrite::saveWorld(World *world, float xpos, float ypos, float scalemult, const char *filename) { //Some Notes: When this method is called, it's assumed that filename is not blank or null std::string addressSAV; @@ -431,13 +477,14 @@ void ReadWrite::saveWorld(World *world, float xpos, float ypos, const char *file //start with saving world settings to compare with current fprintf(fs,"\n"); fprintf(fs,"V= %.2f\n",conf::VERSION); - fprintf(fs,"BRAINSIZE= %i\n", world->BRAINSIZE); + fprintf(fs,"BRAINBOXES= %i\n", world->BRAINBOXES); fprintf(fs,"INPUTS= %i\n", Input::INPUT_SIZE); fprintf(fs,"OUTPUTS= %i\n", Output::OUTPUT_SIZE); - fprintf(fs,"CONNECTIONS= %i\n", CONNS); fprintf(fs,"WIDTH= %i\n", conf::WIDTH); fprintf(fs,"HEIGHT= %i\n", conf::HEIGHT); fprintf(fs,"CELLSIZE= %i\n", conf::CZ); //these saved values up till now are mostly for version control for now + fprintf(fs,"AGENTS_SEE_CELLS= %i\n", world->AGENTS_SEE_CELLS); + fprintf(fs,"AGENTS_DONT_OVERDONATE= %i\n", world->AGENTS_DONT_OVERDONATE); //save settings which have GUI controls fprintf(fs,"MOONLIT= %i\n", world->MOONLIT); fprintf(fs,"MOONLIGHTMULT= %f\n", world->MOONLIGHTMULT); @@ -453,7 +500,8 @@ void ReadWrite::saveWorld(World *world, float xpos, float ypos, const char *file fprintf(fs,"epoch= %i\n", world->current_epoch); fprintf(fs,"mod= %i\n", world->modcounter); fprintf(fs,"xpos= %f\n", xpos); - fprintf(fs,"ypos= %f\n", ypos); //GLView xtranslate and ytranslate + fprintf(fs,"ypos= %f\n", ypos); + fprintf(fs,"scale= %f\n", scalemult); //GLView xtranslate and ytranslate and scalemult for(int cx=0;cxCW;cx++){ //start with the layers for(int cy=0;cyCH;cy++){ float food= world->cells[Layer::PLANTS][cx][cy]; @@ -488,39 +536,45 @@ void ReadWrite::saveWorld(World *world, float xpos, float ypos, const char *file if(!world->isDemo()){ char line[1028], *pos; - //first, check if the last epoch in the saved report matches or is one less than the first one in the current report + //first, check if the last epoch and day in the saved report matches or is one less than the first one in the current report FILE* fr = fopen("report.txt", "r"); FILE* ft = fopen(addressREP.c_str(), "r"); bool append= false; - char text[8]; //for checking - int epoch= 0; - int maxepoch= 0; + char epochtext[8], daytext[8]; //for checking + int epoch; + int maxepoch= -1; + int day; + int maxday= -1; if(ft){ + printf("Old report.txt found, "); while(!feof(ft)){ fgets(line, sizeof(line), ft); pos= strtok(line,"\n"); - sscanf(line, "%s%i%*s", &text, &epoch); - if (strcmp(text, "Epoch:")==0) { - if (epoch>maxepoch) maxepoch= epoch; + sscanf(line, "%s%i%s%i%*s", &epochtext, &epoch, &daytext, &day); + if (strcmp(epochtext, "Epoch:")==0) { + if (epoch>=maxepoch) { + maxepoch= epoch; + maxday = day; + } } } fclose(ft); - if(world->isDebug()) printf("Old report.txt found, and its last epoch was %i.\n", maxepoch); + if(world->isDebug()) printf("its last epoch was %i and day was %i.\n", maxepoch, maxday); //now compare the max epoch from original with the first entry of the new if(fr){ fgets(line, sizeof(line), fr); pos= strtok(line,"\n"); - sscanf(line, "%s%i%*s", &text, &epoch); - if (strcmp(text, "Epoch:")==0) { + sscanf(line, "%s%i%s%i%*s", &epochtext, &epoch, &daytext, &day); + if (strcmp(epochtext, "Epoch:")==0) { //if it is, we append, because this is (very likely) a continuation of that save - if(world->isDebug())printf("Our report.txt starts at epoch %i. ", epoch); - if (epoch==maxepoch || epoch==maxepoch+1){ + if(world->isDebug()) printf("Our report.txt starts at epoch %i and day %i, ", epoch, day); + if ((epoch==maxepoch || epoch==maxepoch+1) && (day==maxday || day==maxday+1 || (maxday==1 && day>1))){ append= true; - printf("Old report.txt found with matching Epoch numbers. Apending to it.\n"); - } else printf("Old report.txt found. Replacing it.\n"); + printf("epoch and day numbers match. Apending to it.\n"); + } else printf("Replacing it.\n"); } rewind(fr); } @@ -537,32 +591,34 @@ void ReadWrite::saveWorld(World *world, float xpos, float ypos, const char *file fprintf(ft, line); } } else { - printf("report.txt didn\'t exist. That\'s... odd...\n"); + printf("report.txt didn\'t exist. That\'s... odd... Stanley, what did you do?!\n"); } fclose(fr); fclose(ft); - } else printf("Demo mode was active; no report data was ready\n"); - //once we've saved, and checked if demo mode was active, we set it to false to make sure we start recording - world->setDemo(false); + if(append) { + //if we appended, then clear the current report so that we can save to the saved file's report once again if we don't restart the program + fopen("report.txt", "w"); + } + + } else printf("Demo mode was active; no report data was ready.\n"); printf("World Saved!\n"); } -void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, const char *filename) +void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, float &scalemult, const char *filename) { //Some Notes: When this method is called, it's assumed that filename is not blank or null std::string address; char line[64], *pos; - char var[16]; + char var[32]; char dataval[16]; int cxl= 0; int cyl= 0; - int mode= -1;//loading mode: -1= off, 0= world, 1= cell, 2= agent, 3= box, 4= connection, 5= eyes, 6= ears + int mode= ReadWriteMode::READY;//loading mode: -1= off, 0= world, 1= cell, 2= agent, 3= box, 4= connection, 5= eyes, 6= ears bool t1= false; //triggers for keeping track of where exactly we are float fileversion= 0; //version 0.05+ upgrade tool - int cellmult= 1; //version 0.04 upgrade tool int i; //integer buffer float f; //float buffer @@ -584,20 +640,27 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co pos= strtok(line,"\n"); sscanf(line, "%s%s", var, dataval); - if(mode==-1){ //mode @ -1 = off + if(mode==ReadWriteMode::READY){ if(strcmp(var, "")==0){ //strcmp= 0 when the arguements equal //if we find a tag, enable world loading and reset. simple - mode= 0; + mode= ReadWriteMode::WORLD; world->reset(); printf("discovered world.\n"); //report status + } else if(strcmp(var, "")==0){ //UNUSED + //if we instead immediately find an tag, switch to agent loading mode - this save was only made with agents and we're meant to only load them + mode= ReadWriteMode::AGENT; + world->sanitize(); + printf("discovered agent database.\n"); //report status } - }else if(mode==0){ //mode @ 0 = world - if(strcmp(var, "V=")==0){ + }else if(mode==ReadWriteMode::WORLD){ + if(strcmp(var, "")==0){ //check for end of world flag, indicating we're done + mode= ReadWriteMode::OFF; + }else if(strcmp(var, "V=")==0){ //version number sscanf(dataval, "%f", &f); if(f!=conf::VERSION) { printf("ALERT: Version Number different! Expected V= %.2f, found V= %.2f\n", conf::VERSION, f); - if(f<0.04) { +/* if(f<0.04) { printf("ALERT: version number < 0.04 detected! Multiplying all cell values by 2!\n"); cellmult= 2; } if (f<0.06) { @@ -607,12 +670,12 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co printf("ALERT: version number < 0.07 detected! World temperature variables are being corrected and Global Climate change is %s!\n", climatestatus); world->CLIMATEBIAS= 0.5; world->CLIMATEMULT= 1; //these settings ensure global climate matches what was used in versions less than 0.07 - } + }*/ //we are not converting old saves in version 0.07 } fileversion= f; - }else if(strcmp(var, "BRAINSIZE=")==0){ + }else if(strcmp(var, "BRAINBOXES=")==0){ sscanf(dataval, "%i", &i); - world->BRAINSIZE= i; + world->BRAINBOXES= i; }else if(strcmp(var, "INPUTS=")==0){ sscanf(dataval, "%i", &i); if(i!=Input::INPUT_SIZE) { @@ -625,12 +688,6 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co printf("ALERT: Brain Output size different! Issues WILL occur! Press enter to try and continue. . .\n"); cin.get(); } - }else if(strcmp(var, "CONNECTIONS=")==0){ - sscanf(dataval, "%i", &i); - if(i!=CONNS) { - printf("ALERT: Brain CONNS per brain box different! Issues WILL occur! Press enter to try and continue. . .\n"); - cin.get(); - } }else if(strcmp(var, "WIDTH=")==0){ //this WILL be loaded soon sscanf(dataval, "%i", &i); @@ -652,6 +709,14 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co printf("ALERT: Cell Size different! Issues WILL occur! Press enter to try and continue. . .\n"); cin.get(); } + }else if(strcmp(var, "AGENTS_SEE_CELLS=")==0){ + sscanf(dataval, "%i", &i); + if(i==1) world->AGENTS_SEE_CELLS= true; + else world->AGENTS_SEE_CELLS= false; + }else if(strcmp(var, "AGENTS_DONT_OVERDONATE=")==0){ + sscanf(dataval, "%i", &i); + if(i==1) world->AGENTS_DONT_OVERDONATE= true; + else world->AGENTS_DONT_OVERDONATE= false; }else if(strcmp(var, "MOONLIT=")==0){ sscanf(dataval, "%i", &i); if(i==1) world->MOONLIT= true; @@ -705,7 +770,6 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co //mod count sscanf(dataval, "%i", &i); world->modcounter= i; - world->ptr= floor((float)i*world->REPORTS_PER_EPOCH/world->FRAMES_PER_EPOCH); //fix for loading saves breaking the epoch alignment of the data graph }else if(strcmp(var, "CLOSED=")==0){ //closed state sscanf(dataval, "%i", &i); @@ -717,25 +781,29 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co // if (i==1) ; // else ; }else if(strcmp(var, "xpos=")==0){ - //veiw screen location x + //view screen location x sscanf(dataval, "%f", &f); xtranslate= f; }else if(strcmp(var, "ypos=")==0){ - //veiw screen location y + //view screen location y sscanf(dataval, "%f", &f); ytranslate= f; + }else if(strcmp(var, "scale=")==0){ + //view screen scalemult + sscanf(dataval, "%f", &f); + scalemult= f; }else if(strcmp(var, "")==0){ //cells tag activates cell reading mode - mode= 1; + mode= ReadWriteMode::CELL; }else if(strcmp(var, "")==0){ //agent tag activates agent reading mode //version 0.05: this is no longer a mode, but rather a whole another method. Should function identically loadAgents(world, fl, fileversion); //when we leave here, should be EOF } - }else if(mode==1){ //mode @ 1 = cell + }else if(mode==ReadWriteMode::CELL){ if(strcmp(var, "")==0){ - //end_cell tag is checked for first, because of else condition - mode= 0; + //"end cell" tag is checked for first, because of else condition + mode= ReadWriteMode::WORLD; if (!t1) printf("loading cells.\n"); //report status t1= true; }else if(strcmp(var, "cx=")==0){ @@ -746,16 +814,16 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co cyl= i; }else if(strcmp(var, "food=")==0){ sscanf(dataval, "%f", &f); - world->cells[Layer::PLANTS][cxl][cyl]= f*cellmult; //cellmult is version 0.04 upgrade tool + world->cells[Layer::PLANTS][cxl][cyl]= f; }else if(strcmp(var, "meat=")==0){ sscanf(dataval, "%f", &f); - world->cells[Layer::MEATS][cxl][cyl]= f*cellmult; + world->cells[Layer::MEATS][cxl][cyl]= f; }else if(strcmp(var, "hazard=")==0){ sscanf(dataval, "%f", &f); - world->cells[Layer::HAZARDS][cxl][cyl]= f*cellmult; + world->cells[Layer::HAZARDS][cxl][cyl]= f; }else if(strcmp(var, "fruit=")==0){ sscanf(dataval, "%f", &f); - world->cells[Layer::FRUITS][cxl][cyl]= f*cellmult; + world->cells[Layer::FRUITS][cxl][cyl]= f; }else if(strcmp(var, "land=")==0){ sscanf(dataval, "%f", &f); world->cells[Layer::ELEVATION][cxl][cyl]= f; @@ -765,7 +833,11 @@ void ReadWrite::loadWorld(World *world, float &xtranslate, float &ytranslate, co fclose(fl); printf("WORLD LOADED!\n"); + world->addEvent("World Loaded!", EventColor::MINT); + + world->setStatsAfterLoad(); + world->processCells(true); world->setInputs(); world->brainsTick(); world->processOutputs(true); diff --git a/SOURCE/ReadWrite.h b/SOURCE/ReadWrite.h index 87ae4bc..36ce8e1 100644 --- a/SOURCE/ReadWrite.h +++ b/SOURCE/ReadWrite.h @@ -4,6 +4,11 @@ #include "World.h" #include +#include "settings.h" +#include "helpers.h" +#include +#include + class ReadWrite { @@ -17,8 +22,8 @@ class ReadWrite void loadAgentFile(World *world, const char *address); //load agent from text file - void saveWorld(World *world, float xpos, float ypos, const char *filename); //save world to text file - void loadWorld(World *world, float &xtranslate, float &ytranslate, const char *filename); //load world from text file + void saveWorld(World *world, float xpos, float ypos, float scalemult, const char *filename); //save world to text file + void loadWorld(World *world, float &xtranslate, float &ytranslate, float &scalemult, const char *filename); //load world from text file const char *ourfile; const char *lastfile; diff --git a/SOURCE/World.cpp b/SOURCE/World.cpp index b8c79b2..a8e28e0 100644 --- a/SOURCE/World.cpp +++ b/SOURCE/World.cpp @@ -37,6 +37,7 @@ World::World() : spawn(); printf("WORLD MADE!\n"); + addEvent("Simulation Started!", EventColor::MINT); } World::~World() @@ -57,10 +58,10 @@ void World::tryPlayAudio(const char* soundFileName, float x, float y, float pitc //Control.cpp { //try to play audio at location x,y if specified (if not, just play 2D audio), and with a specified pitch change multiple - if(DEBUG) printf("Trying to play sound '%s' ... ", soundFileName); - #pragma omp critical - if(soundFileName!=NULL){ - if(dosounds){// && Cull::atCoords(x,y)){ //dosounds is set by GLView and disables when rendering is disabled + if(dosounds){ //dosounds is set by GLView and disables when rendering is disabled + if(DEBUG) printf("Trying to play sound '%s' ... ", soundFileName); + #pragma omp critical + if(soundFileName!=NULL){ ISound* play = 0; if(x==0 && y==0) play= audio->play2D(soundFileName, false, false, true); else play= audio->play3D(soundFileName, vec3df(-x, -y, 0), false, false, true); @@ -70,8 +71,8 @@ void World::tryPlayAudio(const char* soundFileName, float x, float y, float pitc play->setVolume(volume); } } + if(DEBUG) printf("success!\n"); } - if(DEBUG) printf("success!\n"); } @@ -106,42 +107,10 @@ void World::reset() current_epoch= 0; modcounter= 0; idcounter= 0; - INITPLANT= -1; //set these to a crazy value before loading config, so after loading, we read if we changed them! magic - INITFRUIT= -1; - INITMEAT= -1; - INITHAZARD= -1; //try loading constants config. Best do this first in case we change any "constants"! readConfig(); - //tidy up constants - CW= conf::WIDTH/conf::CZ; - CH= conf::HEIGHT/conf::CZ; //note: may add custom variables from loaded saves/config here eventually - int CS= CW*CH; - - if(INITPLANT==-1) INITPLANT = (int)(INITPLANTDENSITY*CS); - if(INITFRUIT==-1) INITFRUIT = (int)(INITFRUITDENSITY*CS); - if(INITMEAT==-1) INITMEAT = (int)(INITMEATDENSITY*CS); - if(INITHAZARD==-1) INITHAZARD = (int)(INITHAZARDDENSITY*CS); - if(INITPLANT>CS) INITPLANT= CS; - if(INITFRUIT>CS) INITFRUIT= CS; - if(INITMEAT>CS) INITMEAT= CS; - if(INITHAZARD>CS) INITHAZARD= CS; - - if(DEBUG){ - printf("INITPLANT: %i\n", INITPLANT); - printf("INITFRUIT: %i\n", INITFRUIT); - printf("INITMEAT: %i\n", INITMEAT); - printf("INITHAZARD: %i\n", INITHAZARD); - } - - if(BRAINSIZE<(Input::INPUT_SIZE+Output::OUTPUT_SIZE)) { - printf("BRAINSIZE config value was too small. It has been reset to min allowed (Inputs+Outputs)\n"); - BRAINSIZE= Input::INPUT_SIZE+Output::OUTPUT_SIZE; - } - - NEAREST= max(max(max(FOOD_SHARING_DISTANCE, SEXTING_DISTANCE), GRABBING_DISTANCE), SPIKELENGTH+MEANRADIUS*3); - sanitize(); //clear all agents //handle layers @@ -154,25 +123,29 @@ void World::reset() } } - //open report file; null it up if it exists. ONLY if we are not in demo mode. Mind you, demo mode could get turned off by config above if(!isDemo()){ + //open report file; null it up if it exists. ONLY if we are not in demo mode. Mind you, demo mode could get turned off by config above FILE* fr = fopen("report.txt", "w"); fclose(fr); - } - ptr= 0; - for(int q=0;q dt; + //Process periodic world events + #if defined(_DEBUG) + if(DEBUG) printf("W"); + #endif + + processWorldTick(); + + //play music! move to CONTROL.CPP + tryPlayMusic(); - //Process periodic events #if defined(_DEBUG) - if(DEBUG) printf("P"); + if(DEBUG) printf("/S"); #endif - //update achievements, write report, record counts, display population events - if(!STATuseracted && pcontrol) { //outside of the report if statement b/c it can happen anytime - addEvent("User, Take the Wheel!", EventColor::MULTICOLOR); - STATuseracted= true;} + //add new agents, if environment isn't closed + processRandomSpawn(); + + #if defined(_DEBUG) + if(DEBUG) printf("/C"); + #endif + + //Process cell changes + processCells(); + + #if defined(_DEBUG) + if(DEBUG) printf("/I"); + #endif + + //give input to every agent. Sets in[] array + setInputs(); + + #if defined(_DEBUG) + if(DEBUG) printf("/B"); + #endif + + //brains tick. computes in[] -> out[] + brainsTick(); + + #if defined(_DEBUG) + if(DEBUG) printf("/N"); + #endif + //reset any counter variables per agent and do other stuff before processOutputs and healthTick + processCounters(); + + #if defined(_DEBUG) + if(DEBUG) printf("/O"); + #endif + + //read output and process consequences of bots on environment. requires out[] + processOutputs(); + + #if defined(_DEBUG) + if(DEBUG) printf("/H"); + #endif + + //process health: + healthTick(); + + #if defined(_DEBUG) + if(DEBUG) printf("/R"); + #endif + + //handle reproduction + processReproduction(); + + #if defined(_DEBUG) + if(DEBUG) printf("/CA"); + #endif + + //process agent-cell interactions + processCellInteractions(); + + #if defined(_DEBUG) + if(DEBUG) printf("/AA"); + #endif + + //process agent-agent interactions + processAgentInteractions(); + //DO NOT add causes of death after this!!! Health should be exactly 0 when leaving this function + + #if defined(_DEBUG) + if(DEBUG) printf("/X"); + #endif + + //process dead agents + processDeath(); + + #if defined(_DEBUG) + if(DEBUG) printf("/"); + #endif + + //move to Control.cpp ... why is this even stored in world?... + vector > >::iterator item= events.begin(); + while (item != events.end()) { + if (item->second.second<-conf::EVENTS_HALFLIFE) { + item= events.erase(item); + } else { + ++item; + } + } + + #if defined(_DEBUG) + if(DEBUG) printf("tick/"); //must occur at end of tick! + #endif + +} + +void World::processWorldTick() +{ //disable demo mode if epoch>0 if(current_epoch>0) setDemo(false); - //disable disable land spawning if the oceans are too small - if(DISABLE_LAND_SPAWN && modcounter==1 && current_epoch==0 && STATlandratio>0.75){ - DISABLE_LAND_SPAWN= false; - addEvent("Land spawning now enabled!", EventColor::CYAN); - addEvent("(you can disable again in menu)", EventColor::CYAN); + //if this is very first tick, + if(modcounter==0 && current_epoch==0){ + setInputs(); //take a snapshot... + brainsTick(); //...push it through the brain... + processOutputs(true); + processOutputs(true); + processOutputs(true); + processOutputs(true); + processOutputs(true); //...and equalize all agent's outputs + + if(DISABLE_LAND_SPAWN && STATlandratio>0.75) { + //disable disable land spawning if the oceans are too small + DISABLE_LAND_SPAWN= false; + addEvent("Land spawning force-enabled!", EventColor::WHITE); + addEvent("(you can disable again in menu)", EventColor::WHITE); + } } - //start reporting process - if (REPORTS_PER_EPOCH>0 && modcounter%(FRAMES_PER_EPOCH/REPORTS_PER_EPOCH)==0) { //if this code is adjusted, please search "ptr" in ReadWrite's load fuction - //first, collect all stats - findStats(); + processReporting(); - numHerbivore[ptr]= getHerbivores(); - numCarnivore[ptr]= getCarnivores(); - numFrugivore[ptr]= getFrugivores(); - numTerrestrial[ptr]= getLungLand(); - numAmphibious[ptr]= getLungAmph(); - numAquatic[ptr]= getLungWater(); - numHybrid[ptr]= getHybrids(); - numDead[ptr]= getDead(); - numTotal[ptr]= getAlive(); - - //events for achievements and stats every report cycle, every other if one was recorded - if(lastptr>=0){ - if(!STATfirstspecies && max(STATbestherbi,max(STATbestfrugi,STATbestcarni))==5) { - addEvent("First Species Suriving!", EventColor::MULTICOLOR); - STATfirstspecies= true;} - if(!STATfirstpopulation && getAlive()>AGENTS_MAX_SPAWN) { - addEvent("First Large Population!", EventColor::MULTICOLOR); - STATfirstpopulation= true;} - if(!STATfirstglobal && STATbestaquatic>=5 && STATbestterran>=5) { - addEvent("First Global Population(s)!", EventColor::MULTICOLOR); - STATfirstglobal= true;} - if(!STATstrongspecies && max(STATbestherbi,max(STATbestfrugi,STATbestcarni))>=500) { - addEvent("First to Generation 500!", EventColor::MULTICOLOR); - STATstrongspecies= true;} - if(!STATstrongspecies2 && max(STATbestherbi,max(STATbestfrugi,STATbestcarni))>=1000) { - addEvent("First to Generation 1000!", EventColor::MULTICOLOR); - STATstrongspecies2= true;} - if(!STATwildspecies && STATstrongspecies) { - int wildest= 0; - for(int i=0; iabs(wildest)) wildest= agents[i].species; - } - if(abs(wildest)>conf::SPECIESID_RANGE){ - addEvent("It\'s over 9000!", EventColor::MULTICOLOR); - STATwildspecies= true; - } - } - if(STATallachieved && FUN==true) FUN= false; - if(!STATallachieved && STATuseracted && STATfirstspecies && STATfirstpopulation && STATfirstglobal && STATstrongspecies && STATstrongspecies2 && STATwildspecies){ - addEvent("All Achievements Achieved!", EventColor::MULTICOLOR); - STATallachieved= true; - FUN= true;} - - int preeventcount= events.size(); //count of events before checks. Will cause next cycle to skip, reducing repeat event flags - - //generic stats between last report and current - if(current_epoch>0 && numTotal[ptr]<=numTotal[lastptr]*0.9-20) { - addEvent("Population Bust!", EventColor::RED);} - if(current_epoch>0 && numTotal[ptr]==AGENTS_MIN_NOTCLOSED && STATfirstspecies && numTotal[lastptr]>AGENTS_MIN_NOTCLOSED*1.1+5) { - addEvent("Population Crash!", EventColor::RED);} - if(numTotal[ptr]>=numTotal[lastptr]*1.1+20) { - //if having a major population boom, check major phenotypes - if(numHerbivore[ptr]>=numHerbivore[lastptr]*1.1+20 && numHerbivore[ptr]>AGENTS_MIN_NOTCLOSED) { - addEvent("Herbivore Bloom", EventColor::GREEN);} - if(numCarnivore[ptr]>=numCarnivore[lastptr]*1.1+20 && numCarnivore[ptr]>AGENTS_MIN_NOTCLOSED) { - addEvent("Carnivore Bloom", EventColor::GREEN);} - if(numFrugivore[ptr]>=numFrugivore[lastptr]*1.1+20 && numFrugivore[ptr]>AGENTS_MIN_NOTCLOSED) { - addEvent("Frugivore Bloom", EventColor::GREEN);} - if(numAmphibious[ptr]>=numAmphibious[lastptr]*1.1+20 && numAmphibious[ptr]>AGENTS_MIN_NOTCLOSED && (numTerrestrial[ptr]>AGENTS_MIN_NOTCLOSED || numAquatic[ptr]>AGENTS_MIN_NOTCLOSED)) { - addEvent("Amphibian Bloom", EventColor::GREEN);} - if(numTerrestrial[ptr]>=numTerrestrial[lastptr]*1.1+20 && numTerrestrial[ptr]>AGENTS_MIN_NOTCLOSED && (numAmphibious[ptr]>AGENTS_MIN_NOTCLOSED || numAquatic[ptr]>AGENTS_MIN_NOTCLOSED)) { - addEvent("Terrestrial Bloom", EventColor::GREEN);} - if(numAquatic[ptr]>=numAquatic[lastptr]*1.1+20 && numAquatic[ptr]>AGENTS_MIN_NOTCLOSED && (numAmphibious[ptr]>AGENTS_MIN_NOTCLOSED || numTerrestrial[ptr]>AGENTS_MIN_NOTCLOSED)) { - addEvent("Aquatic Bloom", EventColor::GREEN);} - } else { - //if not having a major population boom or bust, check slower rates of growth/death - if(ptr>10 && numTotal[ptr]>numTotal[ptr-1] && numTotal[ptr-1]>numTotal[ptr-2] && numTotal[ptr-2]>numTotal[ptr-3] - && numTotal[ptr-3]>numTotal[ptr-4] && numTotal[ptr-4]>numTotal[ptr-5] && numTotal[ptr-5]>numTotal[ptr-6] - && numTotal[ptr-6]>numTotal[ptr-7] && numTotal[ptr-7]>numTotal[ptr-8] && numTotal[ptr-8]>numTotal[ptr-9] - && numTotal[ptr-9]>numTotal[ptr-10]) { - addEvent("Steady Population Bloom", EventColor::GREEN);} - else if(ptr>10 && numTotal[ptr]=FRAMES_PER_EPOCH) { + processNewEpoch(); //resets modcounter= 0 after running + } - if(events.size()>preeventcount) lastptr= -ptr; //new event? skip next cycle of checks; otherwise, update - else lastptr= ptr; - } else lastptr*= -1; - - ptr++; - if(ptr == conf::RECORD_SIZE) ptr = 0; + processClimate(); - //please note: DEMO mode is checked inside this func - writeReport(); + modcounter++; +} - deaths.clear(); - } else if (modcounter%12==0) findStats(); //ocasionally collect stats regardless +void World::processNewEpoch() +{ + modcounter = 0; + current_epoch++; + + //post event that a new epoch has started + std::string eventstring= "Epoch #"; + std::ostringstream ss; + ss << current_epoch; + eventstring.append( ss.str() ); + eventstring.append(" Started"); + addEvent(eventstring, EventColor::BLACK); + + //adjust global mutation rate + processMutationEvent(); +} - //adjust global climate - if(modcounter%(5*FRAMES_PER_DAY)==0) { +void World::processClimate() +{ + if(modcounter%(5*FRAMES_PER_DAY)==0) { //every 5 days, we adjust climate if(CLIMATE){ - int epochmult= modcounter>=FRAMES_PER_EPOCH ? 10 : 1; - CLIMATEBIAS= cap(randn(CLIMATEBIAS, CLIMATE_INTENSITY*epochmult)); //simple cap of bias - we can get stuck at the extremes - CLIMATEMULT= cap(abs(randn(CLIMATEMULT, 2*CLIMATE_INTENSITY*epochmult))); - //more complicated behavior of the mult - take abs randn and cap it - if(modcounter%(int)(FRAMES_PER_EPOCH/2)==0) CLIMATEMULT= 0.25*(CLIMATEMULT_AVERAGE + 3*CLIMATEMULT); - //average the climate mult towards 0.5 occasionally - - if(epochmult!=1) { + float epochmult= modcounter%FRAMES_PER_EPOCH == 0 ? conf::CLIMATE_INTENSITY_EPOCH_MULT : 1.0f; + //simple cap of bias - we can get stuck at the extremes + CLIMATEBIAS= cap(randn(CLIMATEBIAS, CLIMATE_INTENSITY*epochmult)); + + //more complicated behavior of the mult - first, average the climate mult towards 0.5 occasionally + if(modcounter%(int)(FRAMES_PER_EPOCH/2)==0) + CLIMATEMULT= (CLIMATEMULT*conf::CLIMATEMULT_WEIGHT + CLIMATEMULT_AVERAGE) / ((float)(conf::CLIMATEMULT_WEIGHT+1)); + //next, take abs randn and cap it + CLIMATEMULT= cap(abs(randn(CLIMATEMULT, CLIMATE_INTENSITY*epochmult))); + + if(current_epoch == 2050 && agents.size()>9000) { + CLIMATEBIAS = randf(0,1); + CLIMATEMULT = 0.85; + if(epochmult!=1.0) { + addEvent("2050 Climate Target Status:", EventColor::BLACK); + addEvent(" ??? ", EventColor::PURPLE); + } + } + + if(epochmult!=1.0) { if(isHadean()) addEvent("Global Hadean Epoch!", EventColor::ORANGE); else if(isIceAge()) addEvent("Global Ice Age Epoch!", EventColor::CYAN); if(isExtreme()) addEvent("Global Temp Extremes", EventColor::ORANGE); //else if(CLIMATEMULT<0.25) addEvent("Global Uniform Temp", EventColor::YELLOW); } - #if defined(_DEBUG) - printf("Set Climate Bias to %f, and Climate Multiplier to %f\n", CLIMATEBIAS, CLIMATEMULT); - #endif + if(DEBUG) printf("Set Climate Bias to %f, and Climate Multiplier to %f\n", CLIMATEBIAS, CLIMATEMULT); } - } - if (modcounter>=FRAMES_PER_EPOCH) { - modcounter=0; - current_epoch++; - - //adjust global drought multiplier at end of epoch - if(DROUGHTS){ - DROUGHTMULT= randn(DROUGHTMULT, conf::DROUGHT_STDDEV); - if(DROUGHTMULT>DROUGHT_MAX || DROUGHTMULT4) addEvent("Mutation chance > *4!!!", EventColor::LAVENDER); - if(DEBUG) printf("Set Mutation Multiplier to %i\n", MUTEVENTMULT); - } - } - - //play music! - if(dosounds && domusic){ - //unpause music if it's paused - if(currentsong) { - if(currentsong->getIsPaused()) currentsong->setIsPaused(false); - if(currentsong->getVolume()<0.01) currentsong->stop(); + if (DROUGHTS){ + bool beforedrought = isDrought(); + bool beforeovergrowth = isOvergrowth(); + + //every 25 days, we pick a new drought mult + if (modcounter%(25*FRAMES_PER_DAY)==0 && current_epoch > 0) { + DROUGHTMULT= randn(DROUGHTMULT, conf::DROUGHT_STDDEV); + } else if (modcounter%(10*FRAMES_PER_DAY)==0) { + //try to average Droughts down to a random float every 10 days + DROUGHTMULT= (DROUGHTMULT*conf::DROUGHT_WEIGHT_TO_RANDOM+randf(0,1)) / ((float)(conf::DROUGHT_WEIGHT_TO_RANDOM+1)); + } + //average back toward 1.0 if out of range + if (DROUGHTMULT>DROUGHT_MAX || DROUGHTMULT4) + addEvent("Mutation chance > *4!!!", EventColor::LAVENDER); + if(DEBUG) + printf("Set Mutation Multiplier to %i\n", MUTEVENTMULT); + } else { + MUTEVENTMULT = 1; + } +} - //check our list of recently played track indexes to get a new index if needed - for(int i=0; i<5; i++){ - if(songindex==last5songs[i]) { songindex= randi(0,11); i= 0; } - } +void World::findStats() +{ + //clear old stats + STATherbivores= 0; + STATfrugivores= 0; + STATcarnivores= 0; + STATterrans= 0; + STATamphibians= 0; + STATaquatic= 0; + STATalive= 0; + STATdead= 0; + STATspiky= 0; + STAThybrids= 0; + STAThighestgen= 0; + STATlowestgen= INT_MAX; + STATbestherbi= 0; + STATbestfrugi= 0; + STATbestcarni= 0; + STATbestterran= 0; + STATbestamphibious= 0; + STATbestaquatic= 0; + STATbesthybrid= 0; + STATplants= 0; + STATfruits= 0; + STATmeats= 0; + STAThazards= 0; + STATallplant= 0; + STATallfruit= 0; + STATallmeat= 0; + STATallhazard= 0; + + //agents + for (int i=0; i<(int)agents.size(); i++) { + if (agents[i].health>0) { + STATalive++; - std::string songfile= conf::SONGS[songindex]; + if (agents[i].isHerbivore()) { + STATherbivores++; + if (agents[i].gencount>STATbestherbi) STATbestherbi= agents[i].gencount; + } else if (agents[i].isFrugivore()) { + STATfrugivores++; + if (agents[i].gencount>STATbestfrugi) STATbestfrugi= agents[i].gencount; + } else if (agents[i].isCarnivore()) { + STATcarnivores++; + if (agents[i].gencount>STATbestcarni) STATbestcarni= agents[i].gencount; + } - printf("Now playing track: %s", songfile.c_str()); - #if defined(_DEBUG) - printf(", index: %i", songindex); - #endif - printf("\n"); + if (agents[i].isTerrestrial()) { + STATterrans++; + if (agents[i].gencount>STATbestterran) STATbestterran= agents[i].gencount; + } else if (agents[i].isAquatic()) { + STATaquatic++; + if (agents[i].gencount>STATbestaquatic) STATbestaquatic= agents[i].gencount; + } else if (agents[i].isAmphibious()) { + STATamphibians++; + if (agents[i].gencount>STATbestamphibious) STATbestamphibious= agents[i].gencount; + } - currentsong= audio->play2D(songfile.c_str(), false, false, true, ESM_NO_STREAMING); + if (agents[i].gencount>STAThighestgen) STAThighestgen= agents[i].gencount; + if ((agents[i].gencountsetVolume(0.7); + if (agents[i].isSpikey(SPIKE_LENGTH)) STATspiky++; - //finally, add our index to the list of recently played songs - for(int i=4; i>=0; i--){ - if(last5songs[i]==-1) { - last5songs[(i+4)%5]= -1; // this little do-dad will set the next index to -1, allowing us to cycle! - last5songs[i]= songindex; - #if defined(_DEBUG) - printf("added to list of recent songs\n"); - #endif - break; - } - } - } else { - #if defined(_DEBUG) - printf("...failed!\n"); - #endif + if (agents[i].hybrid) { + STAThybrids++; + if (agents[i].gencount>STATbesthybrid) STATbesthybrid= agents[i].gencount; } } - - } else if (currentsong) { - currentsong->setVolume(currentsong->getVolume()*0.92); //fade music if we aren't allowed to play it - if(currentsong->getVolume()<0.001) currentsong->setIsPaused(true); + else STATdead++; } - if(domusic){ - if(timenewsong<-300 || //time-out if it's been a long while since we started a song - (currentsong && currentsong->isFinished())) { //or if the song is finished now - if(currentsong) currentsong->drop(); - currentsong= 0; - timenewsong= randi(15+ceil(modcounter*0.001),50+ceil(modcounter*0.001)); - #if defined(_DEBUG) - printf("setting seconds to next song: %i\n", timenewsong); - #endif + if (STAThighestgen>0) STATinvgenrange= 1/(1.01*STAThighestgen - STATlowestgen); + else STATinvgenrange= 1; + + //agent population graph min/max + MINPOP = 10000000; + MAXPOP = 0; //GLView uses this value to scale the graph vertically + //something-something, intermediate value theorem FTW! + for (int i=0; i MAXPOP) { + MAXPOP= numTotal[i]; + MAXAFTERMIN= true; + } + } - //random seeds/spawns - if ((modcounter%FOODADDFREQ==0 && !CLOSED) || getFood() MAXPOP) { + MAXPOP= alivenow; + MAXAFTERMIN= true; } - if (modcounter%FRUITADDFREQ==0 && randf(0,1)0){ - if (cells[Layer::PLANTS][cx][cy]>FRUITREQUIRE) { - cells[Layer::FRUITS][cx][cy]= 1.0; - break; - } - } else { //also handle negative Drought multipliers by reducing random fruit cells - cells[Layer::FRUITS][cx][cy]= cap(cells[Layer::FRUITS][cx][cy]-0.1); - break; - } + + //cell layers + for(int i=0;i=0.25) STATplants++; + if(cells[Layer::FRUITS][i][j]>=0.25) STATfruits++; + if(cells[Layer::MEATS][i][j]>=0.25) STATmeats++; + if(cells[Layer::HAZARDS][i][j]>=0.25) STAThazards++; + STATallplant+= cells[Layer::PLANTS][i][j]; + STATallfruit+= cells[Layer::FRUITS][i][j]; + STATallmeat+= cells[Layer::MEATS][i][j]; + STATallhazard+= cells[Layer::HAZARDS][i][j]; } } +} - float invCW= 1/(float)CW; - float daytime= (modcounter+current_epoch*FRAMES_PER_EPOCH)*2*M_PI/FRAMES_PER_DAY; - #pragma omp parallel for schedule(dynamic) - for(int cy=0; cy<(int)CH;cy++){ - float tempzone= CLIMATE_KILL_FLORA ? calcTempAtCoord(cy) : 0.5; //calculate the y-coord's temp once per row - for(int cx=0; cx<(int)CW;cx++){ - float plant = cells[Layer::PLANTS][cx][cy]; - float fruit = cells[Layer::FRUITS][cx][cy]; - float meat = cells[Layer::MEATS][cx][cy]; - float hazard = cells[Layer::HAZARDS][cx][cy]; - - //plant ops - if (plant>0) { - float tempmult= CLIMATE_KILL_FLORA ? 2-cap(min(tempzone*2+0.5,2.5-2*tempzone)) : 1.0; - plant-= FOODDECAY*tempmult; //food quantity is changed by FOODDECAY, which is doubled in bad temperature regions (arctic and hadean) - if (hazard>0) { - plant+= FOODGROWTH*max(0.0f,DROUGHTMULT)*hazard; //food grows out of waste/hazard, limited by low DROUGHTMULT - } +void World::processReporting() +{ + //update achievements, write report, record counts, display population events + if (!STATuseracted && pcontrol) { //outside of the report if statement b/c it can happen anytime + addEvent("User, Take the Wheel!", EventColor::MULTICOLOR); + STATuseracted= true; + } - if(CLIMATE_KILL_FLORA) tempmult= 2*cap(min(tempzone*2,2-2*tempzone)-0.25); //adjust the temp mult for plant spread - if (randf(0,1)=0.5) { //plant grows from itself - //food seeding - int ox= randi(cx-1-FOODRANGE,cx+2+FOODRANGE); - int oy= randi(cy-1-FOODRANGE,cy+2+FOODRANGE); - if (ox<0) ox+= CW; - if (ox>CW-1) ox-= CW; - if (oy<0) oy+= CH; - if (oy>CH-1) oy-= CH; //code up to this point ensures world edges are crossed and not skipped - if (cells[Layer::PLANTS][ox][oy]<=0.75) cells[Layer::PLANTS][ox][oy]+= 0.25; + //start reporting process + if (REPORTS_PER_EPOCH>0 && modcounter%(FRAMES_PER_EPOCH/REPORTS_PER_EPOCH)==0) { + //first, collect all stats + findStats(); + + //pop the front of all the vectors + pop_front(graphGuides); + pop_front(numTotal); + pop_front(numDead); + pop_front(numHerbivore); + pop_front(numCarnivore); + pop_front(numFrugivore); + pop_front(numTerrestrial); + pop_front(numAmphibious); + pop_front(numAquatic); + pop_front(numHybrid); + + //push back all new values + int guideline; + if(modcounter%FRAMES_PER_EPOCH==0) guideline = GuideLine::EPOCH; + else if (modcounter%FRAMES_PER_DAY==0) guideline = GuideLine::DAY; + else guideline = GuideLine::NONE; + graphGuides.push_back(guideline); + + numTotal.push_back(getAlive()); + numDead.push_back(getDead()); + numHerbivore.push_back(getHerbivores()); + numCarnivore.push_back(getCarnivores()); + numFrugivore.push_back(getFrugivores()); + numTerrestrial.push_back(getLungLand()); + numAmphibious.push_back(getLungAmph()); + numAquatic.push_back(getLungWater()); + numHybrid.push_back(getHybrids()); + + //events for achievements and stats every epoch + triggerStatEvents(); + + //once-epoch events tags (skipping if the 0th slot is totally zero, indicating a load or program start) + if(modcounter%FRAMES_PER_EPOCH == 0 && numTotal[0] != 0 && current_epoch > 0){ + //int preeventcount= events.size(); //count of events before checks. Will cause next cycle to skip, reducing repeat event flags + int ptr = REPORTS_PER_EPOCH-1; + + //generic stats between start of epoch and current + if(!MAXAFTERMIN && MINPOP <= AGENTS_MIN_NOTCLOSED && STATfirstspecies) { + //population fell and the minimum is at or below min_agents after the First Species alert was fired + addEvent("Population Crash Epoch!", EventColor::RED); + STATfirstspecies= false; //reset first species alert + + } else if (!MAXAFTERMIN && MINPOP <= MAXPOP*0.5) { + //population fell and the minimum is half the maximum or lower + addEvent("Population Bust Epoch!", EventColor::RED); + + } else if (MAXAFTERMIN && MAXPOP >= MINPOP*1.3) { + //population rose and the maximum exceeds the minimum by a large amount (130%) + if (MAXPOP >= MINPOP*2 && MINPOP > AGENTS_MIN_NOTCLOSED+5) { + //check for a huge population boom (doubling) as long as the min was greater than the base state + addEvent("Population *2 (or more) Epoch!", EventColor::BLUE); } - } - cells[Layer::PLANTS][cx][cy]= cap(plant); - //end plant - //meat ops - if (meat>0) { - meat -= MEATDECAY; //consider: meat decay effected by direct tempzone? - } - cells[Layer::MEATS][cx][cy]= cap(meat); - //end meat + //check major phenotypes for blooms or branches: + //CONSIDER: phenotype min & max trackers (not public values, just local) + bool anyphenotype = false; + //stomach phenotype + if(numHerbivore[ptr] >= numHerbivore[0]*1.3 && numHerbivore[ptr] > AGENTS_MIN_NOTCLOSED) { + if(numCarnivore[0] + numFrugivore[0] > numHerbivore[0] && numTotal[0] > AGENTS_MIN_NOTCLOSED+5) + addEvent("Herbivore Branch Epoch", EventColor::GREEN); //if the other phenotypes were >, we branched + else addEvent("Herbivore Bloom Epoch", EventColor::GREEN); //otherwise, we bloomed + anyphenotype = true; + } + if(numCarnivore[ptr] >= numCarnivore[0]*1.3 && numCarnivore[ptr] > AGENTS_MIN_NOTCLOSED) { + if(numHerbivore[0] + numFrugivore[0] > numCarnivore[0] && numTotal[0] > AGENTS_MIN_NOTCLOSED+5) + addEvent("Carnivore Branch Epoch", EventColor::GREEN); + else addEvent("Carnivore Bloom Epoch", EventColor::GREEN); + anyphenotype = true; + } + if(numFrugivore[ptr] >= numFrugivore[0]*1.3 && numFrugivore[ptr] > AGENTS_MIN_NOTCLOSED) { + if(numHerbivore[0] + numCarnivore[0] > numFrugivore[0] && numTotal[0] > AGENTS_MIN_NOTCLOSED+5) + addEvent("Frugivore Branch Epoch", EventColor::GREEN); + else addEvent("Frugivore Bloom Epoch", EventColor::GREEN); + anyphenotype = true; + } + //lung phenotype + if(numAmphibious[ptr] >= numAmphibious[0]*1.3 && numAmphibious[ptr] > AGENTS_MIN_NOTCLOSED) { + if(numAquatic[0] + numTerrestrial[0] > numAmphibious[0] && numTotal[0] > AGENTS_MIN_NOTCLOSED+5) + addEvent("Amphibian Branch Epoch", EventColor::GREEN); + else addEvent("Amphibian Bloom Epoch", EventColor::GREEN); + anyphenotype = true; + } + if(numTerrestrial[ptr] >= numTerrestrial[0]*1.3 && numTerrestrial[ptr] > AGENTS_MIN_NOTCLOSED) { + if(numAquatic[0] + numAmphibious[0] > numTerrestrial[0] && numTotal[0] > AGENTS_MIN_NOTCLOSED+5) + addEvent("Terrestrial Branch Epoch", EventColor::GREEN); + else addEvent("Terrestrial Bloom Epoch", EventColor::GREEN); + anyphenotype = true; + } + if(numAquatic[ptr] >= numAquatic[0]*1.3 && numAquatic[ptr] > AGENTS_MIN_NOTCLOSED) { + if(numTerrestrial[0] + numAmphibious[0] > numAquatic[0] && numTotal[0] > AGENTS_MIN_NOTCLOSED+5) + addEvent("Aquatic Branch Epoch", EventColor::GREEN); + else addEvent("Aquatic Bloom Epoch", EventColor::GREEN); + anyphenotype = true; + } - //fruit ops - if (fruit>0) { - if (plant<=FRUITREQUIRE || DROUGHTMULT<0){ - fruit-= FRUITDECAY; //fruit decays, double if lack of plant life or DROUGHTMULT is negative + if(!anyphenotype) { + //if none of the major phenotypes triggered, we just had a generic population bloom + addEvent("Population Bloom Epoch", EventColor::GREEN); } - fruit-= FRUITDECAY; - } - cells[Layer::FRUITS][cx][cy]= cap(fruit); - //end fruit - - //hazard = cells[Layer::HAZARDS]... - if (hazard>0 && hazard<=0.9){ - hazard-= HAZARDDECAY; //hazard decays - } else if (hazard>0.9 && randf(0,1)<0.0625){ - hazard= 90*(hazard-0.99); //instant hazards will be reset to proportionate value + } else { + //if not having a major population boom or bust, check slower rates of growth/death + //if(numTotal[ptr] > numTotal[0]*1.2) addEvent("Slight Increase Population Epoch", EventColor::BLUE); + //else if(numTotal[ptr] < numTotal[0]*0.8) addEvent("Slight Decrease Population Epoch", EventColor::BLUE); + //else if(numTotal[ptr] < AGENTS_MIN_NOTCLOSED*1.2) addEvent("Minimum Population Epoch", EventColor::BLUE); + //else + addEvent("Steady Population Epoch", EventColor::BLUE); } - cells[Layer::HAZARDS][cx][cy]= cap(hazard); - //end hazard - - //light ops - //if we are moonlit and moonlight happens to be 1.0, then the light layer is useless. Set all values to 1 for rendering - cells[Layer::LIGHT][cx][cy]= (MOONLIT && MOONLIGHTMULT==1.0) ? 1.0 : - cap(0.6+sin((cx*2*M_PI)*invCW - daytime)); - //cap(0.6+sin((cx*2*M_PI)/CW-(modcounter+current_epoch*FRAMES_PER_EPOCH)*2*M_PI/FRAMES_PER_DAY)); - //end light } - } - - #if defined(_DEBUG) - if(DEBUG) printf("/"); - #endif - - //give input to every agent. Sets in[] array - setInputs(); - //brains tick. computes in[] -> out[] - brainsTick(); - - //reset any counter variables per agent and do other stuff before processOutputs and healthTick - #pragma omp parallel for - for (int i=0; i<(int)agents.size(); i++) { - Agent* a= &agents[i]; + //please note: DEMO mode is checked inside this func + writeReport(); - //process indicator, used in drawing - if(a->indicator>0) a->indicator-= 0.5; - if(a->indicator<0) a->indicator-= 1; + deaths.clear(); + } else if (modcounter%12==0) findStats(); //ocasionally collect stats regardless +} - //process carcass counter, which keeps dead agents on the world while meat is under them - if(a->carcasscount>=0) a->carcasscount++; +void World::triggerStatEvents(bool showevents) +{ + if(!STATfirstspecies && max(STATbestherbi,max(STATbestfrugi,STATbestcarni))==5) { + if(showevents) addEvent("First Species Suriving!", EventColor::MULTICOLOR); + STATfirstspecies= true;} + if(!STATfirstpopulation && getAlive()>AGENTS_MAX_SPAWN) { + if(showevents) addEvent("First Large Population!", EventColor::MULTICOLOR); + STATfirstpopulation= true;} + if(!STATfirstglobal && STATbestaquatic>=5 && STATbestterran>=5) { + if(showevents) addEvent("First Global Population(s)!", EventColor::MULTICOLOR); + STATfirstglobal= true;} + if(!STATstrongspecies && max(STATbestherbi,max(STATbestfrugi,STATbestcarni))>=500) { + if(showevents) addEvent("First to Generation 500!", EventColor::MULTICOLOR); + STATstrongspecies= true;} + if(!STATstrongspecies2 && max(STATbestherbi,max(STATbestfrugi,STATbestcarni))>=1000) { + if(showevents) addEvent("First to Generation 1000!", EventColor::MULTICOLOR); + STATstrongspecies2= true;} + if(!STATwildspecies && STATstrongspecies) { + int wildest= 0; + for(int i=0; iabs(wildest)) wildest= agents[i].species; + } + if(abs(wildest)>conf::SPECIESID_RANGE){ + if(showevents) addEvent("It\'s over 9000!", EventColor::MULTICOLOR); + STATwildspecies= true; + } + } + if(STATallachieved && FUN==true) FUN= false; + if(!STATallachieved && STATuseracted && STATfirstspecies && STATfirstpopulation && STATfirstglobal && STATstrongspecies && STATstrongspecies2 && STATwildspecies){ + if(showevents) addEvent("All Achievements Achieved!", EventColor::MULTICOLOR); + STATallachieved= true; + FUN= true; + } - //process jaw renderer - if(a->jawrend>0 && a->jawPosition==0 || a->jawrend==conf::JAWRENDERTIME) a->jawrend-=1; +} - //if alive... - if(a->health>0){ - //reduce fresh kill flag - if(a->freshkill>0) a->freshkill-= 1; - - //Live mutations - for(int m=0; mliveMutate(MUTEVENTMULT); - if(a->id==SELECTION) addEvent("The Selected Agent Was Mutated!", EventColor::PURPLE); //control.cpp - want this to be supressed when in fast mode - STATlivemutations++; - if(DEBUG) printf("Live Mutation Event\n"); +void World::processCells(bool prefire) +{ + if(conf::CELL_TICK_RATE!=0) { + //random seeds/spawns + if ((modcounter%PLANT_ADD_FREQ==0 && !CLOSED) || getFood()0){ + if (cells[Layer::PLANTS][cx][cy]>FRUIT_PLANT_REQUIRE) { + cells[Layer::FRUITS][cx][cy]= 1.0; + break; + } + } else { //also handle negative Drought multipliers by reducing random fruit cells + cells[Layer::FRUITS][cx][cy]= cap(cells[Layer::FRUITS][cx][cy]-0.1); + break; } } + } - //Age goes up! - if (modcounter%(FRAMES_PER_DAY/10)==0) a->age+= 1; + float invCW= 1/(float)CW; + float daytime= (modcounter+current_epoch*FRAMES_PER_EPOCH)*2*M_PI/FRAMES_PER_DAY; - //update jaw - if(a->jawPosition>0) { - a->jawPosition*= -1; //jaw is an instantaneous source of damage. Reset to a negative number if positive - a->jawrend= conf::JAWRENDERTIME; //set render timer + #pragma omp parallel for schedule(dynamic) + for(int cy=0; cy<(int)CH;cy++){ + float tempzone= CLIMATE_AFFECT_FLORA ? calcTempAtCoord(cy) : 0.5; //calculate the y-coord's temp once per row + + for(int cx=0; cx<(int)CW;cx++){ + //basic simulation spread function; calculate every other cell every other tick, except modcounter= first frame + if((cx+cy)%conf::CELL_TICK_RATE!=modcounter%conf::CELL_TICK_RATE && modcounter!=1 && !prefire) continue; + + float plant = cells[Layer::PLANTS][cx][cy]; + float fruit = cells[Layer::FRUITS][cx][cy]; + float meat = cells[Layer::MEATS][cx][cy]; + float hazard = cells[Layer::HAZARDS][cx][cy]; + + //plant ops + if (plant>0 && !prefire) { + float tempmult= CLIMATE_AFFECT_FLORA ? 2-cap(2*min(tempzone+0.5-conf::CLIMATE_KILL_FLORA_ZONE,1.5-conf::CLIMATE_KILL_FLORA_ZONE-tempzone)) : 1.0; + plant-= PLANT_DECAY*conf::CELL_TICK_RATE*tempmult; //food quantity is changed by PLANT_DECAY, which is doubled in bad temperature regions (arctic and hadean) + if (hazard>0) { + plant+= PLANT_GROWTH*conf::CELL_TICK_RATE*max(0.0f,DROUGHTMULT)*hazard; //food grows out of waste/hazard, limited by low DROUGHTMULT + } - //once negative, jaw moves toward to 0, slowly if near -1, faster once closer to 0 - } else if (a->jawPosition<0) a->jawPosition= min(0.0, a->jawPosition + 0.01*(2 + a->jawPosition)); + if(CLIMATE_AFFECT_FLORA) tempmult= 2*cap(2*min(tempzone,1-tempzone)-conf::CLIMATE_KILL_FLORA_ZONE); //adjust the temp mult for plant spread + if (randf(0,1)=0.5) { //plant grows from itself + //food seeding + int ox= randi(cx-1-PLANT_RANGE,cx+2+PLANT_RANGE); + int oy= randi(cy-1-PLANT_RANGE,cy+2+PLANT_RANGE); + if (ox<0) ox+= CW; + if (ox>CW-1) ox-= CW; + if (oy<0) oy+= CH; + if (oy>CH-1) oy-= CH; //code up to this point ensures world edges are crossed and not skipped + if (cells[Layer::PLANTS][ox][oy]<=0.75) cells[Layer::PLANTS][ox][oy]+= 0.25; + } + } + cells[Layer::PLANTS][cx][cy]= cap(plant); - //update center render mode. Asexual pulls toward 0, female sexual pulls toward 1, male sexual towards 2 - if(a->isAsexual()) a->centerrender-= 0.01*(a->centerrender); - else if(a->isMale()) a->centerrender-= 0.01*(a->centerrender-2); - else a->centerrender-= 0.01*(a->centerrender-1); - a->centerrender= cap(a->centerrender*0.5)*2; //duck the counter under a cap to allow range [0,2] + //meat ops + if (meat>0 && !prefire) { + meat -= MEAT_DECAY*conf::CELL_TICK_RATE; //consider: meat decay effected by direct tempzone? + } + cells[Layer::MEATS][cx][cy]= cap(meat); + + //fruit ops + if (fruit>0 && !prefire) { + if (plant<=FRUIT_PLANT_REQUIRE || DROUGHTMULT<0){ + fruit-= FRUIT_DECAY*conf::CELL_TICK_RATE; //fruit decays, double if lack of plant life or DROUGHTMULT is negative + } + fruit-= FRUIT_DECAY*conf::CELL_TICK_RATE; + } + cells[Layer::FRUITS][cx][cy]= cap(fruit); - //exhaustion gets increased - float boostmult= a->boost ? BOOSTEXAUSTMULT : 1; - a->exhaustion= max((float)0,a->exhaustion + a->getOutputSum()*boostmult + BASEEXHAUSTION)*EXHAUSTION_MULT; + //hazard = cells[Layer::HAZARDS]... + if (hazard>0 && hazard<=0.9 && !prefire){ + hazard-= HAZARD_DECAY*conf::CELL_TICK_RATE; //hazard decays + } else if (hazard>0.9 && randf(0,1)<0.0625 && !prefire){ + hazard= 90*(hazard-0.99); //instant hazards will be reset to proportionate value + } + cells[Layer::HAZARDS][cx][cy]= cap(hazard); - //temp discomfort gets re-calculated intermittently based on size, to simulate heat absorption/release - if (modcounter%(int)ceil(10+2*a->radius)==0){ - //calculate temperature at the agents spot. (based on distance from horizontal equator) - float dd= calcTempAtCoord(a->pos.y); - a->discomfort+= 0.1*(abs(dd-a->temperature_preference) - a->discomfort); - if (a->discomfort<0.01) a->discomfort= 0; + //light ops + //if we are moonlit and moonlight happens to be 1.0, then the light layer is useless. Set all values to 1 for rendering + cells[Layer::LIGHT][cx][cy]= (MOONLIT && MOONLIGHTMULT==1.0) ? 1.0 : + cap(0.6+sin((cx*2*M_PI)*invCW - daytime)); + //cap(0.6+sin((cx*2*M_PI)/CW-(modcounter+current_epoch*FRAMES_PER_EPOCH)*2*M_PI/FRAMES_PER_DAY)); } } } +} - //read output and process consequences of bots on environment. requires out[] - processOutputs(); +void World::tryPlayMusic() +//CONTROL.CPP!!! +{ + if(dosounds && domusic){ + //unpause music if it's paused + if(currentsong) { + if(currentsong->getIsPaused()) currentsong->setIsPaused(false); + if(currentsong->getVolume()<0.01) currentsong->stop(); + } - //process health: - healthTick(); - - //handle reproduction - //do not omp any of this! - if (modcounter%5==0){ - for (int i=0; i<(int)agents.size(); i++) { - if (agents[i].repcounter<0 && agents[i].health>=MINMOMHEALTH) { - //agent is healthy and is ready to reproduce. Now to decide how... + //if we are ready for a new song, pick a new song + if(timenewsong == 0 && !currentsong) { + int songindex = randi(0,8); + if(current_epoch == 0 && isDemo() && last5songs[0] == -1) + songindex = 0; //always play the first song first in demo mode; it's our theme song! - if(SEXTING_DISTANCE>0 && !agents[i].isAsexual()){ - if(agents[i].isMale()) continue;; //'fathers' cannot themselves reproduce - - for (int j=0; j<(int)agents.size(); j++) { - if(i==j || agents[j].isAsexual() || agents[j].repcounter>0) continue; - float d= (agents[i].pos-agents[j].pos).length(); - float deviation= abs(agents[i].species - agents[j].species); //species deviation check - if (d<=SEXTING_DISTANCE && deviation<=agents[i].kinrange) { //uses mother's kinrange - //this adds agent[i].numbabies to world, with two parents - if(DEBUG) printf("Attempting to have children..."); - reproduce(i, j); //reproduce resets mother's rep counter, not father's - if(agents[i].id==SELECTION) addEvent("Selected Agent Sexually Reproduced", EventColor::BLUE); - tryPlayAudio(conf::SFX_SMOOCH1, agents[i].pos.x, agents[i].pos.y, randn(1.0,0.15)); - if(DEBUG) printf(" Success!\n"); - break; - continue; - } + //check our list of recently played track indexes to get a new index if needed + int tries = 0; + while(tries < 25) { + bool songplayed = false; + for(int i=0; i<5; i++){ + if(songindex == last5songs[i]) { + songplayed = true; + songindex = randi(0,conf::NUM_SONGS); + tries++; + break; //if the song was in last 5 songs, pick a new one, increment the tries counter, and try again } - } else { - //this adds agents[i].numbabies to world, but with just one parent (budding) - if(DEBUG) printf("Attempting budding..."); - reproduce(i, i); - if(agents[i].id==SELECTION) addEvent("Selected Agent Assexually Budded", EventColor::NEON); - tryPlayAudio(conf::SFX_PLOP1, agents[i].pos.x, agents[i].pos.y, randn(1.0,0.15)); - if(DEBUG) printf(" Success!\n"); - continue; } + if (!songplayed) break; } - if(FUN && randf(0,1)<0.3){ - if(agents[i].indicator<=0) agents[i].indicator= 60; - agents[i].ir= randf(0,1); - agents[i].ig= randf(0,1); - agents[i].ib= randf(0,1); - } - } - } - - processInteractions(); - //DO NOT add causes of death after this!!! Health should be exactly 0 when leaving this function - - for (int i=0; i<(int)agents.size(); i++) { - //process dead agents. first do some checks and post alerts as needed - if(agents[i].health<0) printf("Please check the code: an agent unexpectedly had negative health when it should have had exactly zero\n"); - if (agents[i].health==0 && agents[i].carcasscount==0) { - //world is only now picking up the fact that the agent died from the agent itself - if (SELECTION==agents[i].id){ - printf("The Selected Agent was %s!\n", agents[i].death.c_str()); - addEvent("The Selected Agent Died!"); - - //play audio clip of death. At position 0,0 (no position) if we had the agent selected, otherwise, use world coords - tryPlayAudio(conf::SFX_DEATH1, 0, 0, randn(1.0,0.05), 0.8); - } else tryPlayAudio(conf::SFX_DEATH1, agents[i].pos.x, agents[i].pos.y, randn(1.0,0.15)); - - //next, distribute meat - int cx= (int) agents[i].pos.x/conf::CZ; - int cy= (int) agents[i].pos.y/conf::CZ; - - float meat= cells[Layer::MEATS][cx][cy]; - float agemult= 1.0; - float freshmult= 0.5; //if this agent wasnt freshly killed, default to 50% - float stomachmult= 1+(conf::CARNIVORE_MEAT_EFF-1)*sqrt(agents[i].stomach[Stomach::MEAT]); //carnivores give less, specified by CARNIVORE_MEAT_EFF - //this math re-aranged from x-(x-1)*sqrt(stomach[MEAT])/x, where "x" was the inverted efficiency value (was x=4, or efficiency of 0.25) - - if(agents[i].age0) freshmult= 1.0; //agents which were spiked recently will give full meat + + std::string songfile = conf::SONGS[songindex]; - meat+= MEATVALUE*agemult*freshmult*stomachmult; - cells[Layer::MEATS][cx][cy]= cap(meat); + printf("Now playing track: %s", songfile.c_str()); + #if defined(_DEBUG) + printf(", index: %i", songindex); + #endif - //collect all the death causes from all dead agents - deaths.push_back(agents[i].death); - } - } + currentsong= audio->play2D(songfile.c_str(), false, false, true, ESM_NO_STREAMING); - vector::iterator iter= agents.begin(); - while (iter != agents.end()) { - if (iter->health <=0 && iter->carcasscount >= conf::CORPSE_FRAMES) { - if(iter->id == SELECTION) addEvent("The Selected Agent has decayed", EventColor::BROWN); - iter= agents.erase(iter); - } else { - ++iter; - } - } + if(currentsong){ + currentsong->setVolume(0.7); - //add new agents, if environment isn't closed - if (!CLOSED) { - int alive= agents.size()-getDead(); - //make sure environment is always populated with at least AGENTS_MIN_NOTCLOSED bots - if (alive=0; i--){ + if(last5songs[i]==-1) { + last5songs[(i+4)%5]= -1; // this little do-dad will set the next index to -1, allowing us to cycle! + last5songs[i]= songindex; + #if defined(_DEBUG) + printf(", added to list of recent songs\n"); + #endif + break; + } + } + } else { + #if defined(_DEBUG) + printf("...failed!\n"); + #endif + } + printf("\n"); } - } - vector > >::iterator item= events.begin(); - while (item != events.end()) { - if (item->second.second<-conf::EVENTS_HALFLIFE) { - item= events.erase(item); - } else { - ++item; - } + } else if (currentsong) { + currentsong->setVolume(currentsong->getVolume()*0.92); //fade music if we aren't allowed to play it + if(currentsong->getVolume()<0.001) currentsong->setIsPaused(true); } - #if defined(_DEBUG) - if(DEBUG) printf("tick/"); //must occur at end of tick! - #endif - + if(domusic){ + if(timenewsong<-300 || //time-out if it's been a long while since we started a song + (currentsong && currentsong->isFinished())) { //or if the song is finished now + if(currentsong) currentsong->drop(); + currentsong= 0; + timenewsong= randi(15+ceil(modcounter*0.001),50+ceil(modcounter*0.001)); + #if defined(_DEBUG) + printf("setting seconds to next song: %i\n", timenewsong); + #endif + } + } else timenewsong= 10; } void World::setInputs() { - #if defined(_DEBUG) - if(DEBUG) printf("I"); - #endif - float PI8=M_PI/8/2; //pi/8/2 float PI38= 3*PI8; //3pi/8/2 float PI4= M_PI/4; @@ -1048,181 +1196,226 @@ void World::setInputs() //our cell position int scx= (int)(a->pos.x/conf::CZ);//, 0, conf::WIDTH/conf::CZ); int scy= (int)(a->pos.y/conf::CZ);//, 0, conf::HEIGHT/conf::CZ); - - //HEALTH - a->in[Input::HEALTH]= cap(a->health/conf::HEALTH_CAP); //if we start using mutable health maximums, use that instead of HEALTH_CAP - //SOUND SMELL EYES - float light= (MOONLIT) ? max(MOONLIGHTMULT, cells[Layer::LIGHT][scx][scy]) : cells[Layer::LIGHT][scx][scy]; //grab min light level for conditions + float light = (MOONLIT) ? max(MOONLIGHTMULT, cells[Layer::LIGHT][scx][scy]) : cells[Layer::LIGHT][scx][scy]; //grab min light level for conditions - vector r(NUMEYES,conf::LIGHT_AMBIENT_PERCENT*light); - vector g(NUMEYES,conf::LIGHT_AMBIENT_PERCENT*light); - vector b(NUMEYES,conf::LIGHT_AMBIENT_PERCENT*light); - + vector r(NUMEYES, 0); + vector g(NUMEYES, 0); + vector b(NUMEYES, 0); + if (!AGENTS_SEE_CELLS && a->isTiny()) { + r.assign(NUMEYES, AMBIENT_LIGHT_PERCENT*light); + g.assign(NUMEYES, AMBIENT_LIGHT_PERCENT*light); + b.assign(NUMEYES, AMBIENT_LIGHT_PERCENT*light); + } float smellsum=0; - vector hearsum(NUMEARS,0); - - //BLOOD ESTIMATOR float blood= 0; - //cell sense - int minx= max((scx-DIST/conf::CZ/2),(float)0); - int maxx= min((scx+1+DIST/conf::CZ/2),(float)CW); - int miny= max((scy-DIST/conf::CZ/2),(float)0); - int maxy= min((scy+1+DIST/conf::CZ/2),(float)CH); - float fruit= 0, meat= 0, hazard= 0, water= 0; - //cell "smelling": a faster/better food- & hazard-revealing solution than adding to eyesight - for(scx=minx;scxsmell_mod*dimmensions; - meat*= a->smell_mod*dimmensions; - hazard*= a->smell_mod*dimmensions; - water*= a->smell_mod*dimmensions; + //cell sense min-max's + float distmult= conf::SMELL_DIST_MULT/(float)conf::CZ/2; + int minx= max((scx-MAX_SENSORY_DISTANCE*distmult),(float)0); + int maxx= min((scx+1+MAX_SENSORY_DISTANCE*distmult),(float)CW); + int miny= max((scy-MAX_SENSORY_DISTANCE*distmult),(float)0); + int maxy= min((scy+1+MAX_SENSORY_DISTANCE*distmult),(float)CH); + + //---AGENT-CELL SENSES---// + for(int tcx= minx; tcxeye_see_cell_mod; - /* CELL EYESIGHT CODE - if (cells[Layer::PLANTS][scx][scy]==0 && cells[Layer::MEATS][scx][scy]==0 && cells[Layer::HAZARDS][scx][scy]==0) continue; - Vector2f cellpos= Vector2f((float)(scx*conf::CZ+conf::CZ/2),(float)(scy*conf::CZ+conf::CZ/2)); //find midpoint of the cell - float d= (a->pos-cellpos).length(); + Vector2f cellpos = Vector2f((float)(tcx*conf::CZ+conf::CZ/2),(float)(tcy*conf::CZ+conf::CZ/2)); //find midpoint of the cell + float d = max((a->pos - cellpos).length(), a->radius); // can't see cells as being inside of us; they're around us - if (dpos).get_angle(); + float ang= (cellpos - a->pos).get_angle(); for(int q=0;qangle + a->eyedir[q]; - if (aa<-M_PI) aa += 2*M_PI; - if (aa>M_PI) aa -= 2*M_PI; + if(a->isTiny() && !a->isTinyEye(q)){ //small agents have half-count of eyes, the rest just keep the ambient light values they got + continue; + } + + float eye_absolute_angle = a->angle + a->eyedir[q]; //get eye's absolute (world) angle + // a->angle is in [-pi,pi], a->eyedir[] is in [0,2pi]. it's possible to be > pi but not > 3*pi or < -pi + if (eye_absolute_angle >= M_PI) eye_absolute_angle -= 2*M_PI; //correct if > pi - float diff1= aa- angle; - if (fabs(diff1)>M_PI) diff1= 2*M_PI- fabs(diff1); - diff1= fabs(diff1); + //remember, eye_absolute_angle is in [-pi,pi], and ang is in [-pi,pi] + float eye_target_angle = eye_absolute_angle - ang; //get the difference in absolute angles between the eye and the cell + if (eye_target_angle >= M_PI) eye_target_angle -= 2*M_PI; //correct to within [-pi,pi] again, because a difference of 2pi is no difference at all + else if (eye_target_angle < -M_PI) eye_target_angle += 2*M_PI; + eye_target_angle = fabs(eye_target_angle); - float fov = a->eyefov[q]; - if (diff1eye_see_cell_mod*(fabs(fov-diff1)/fov)*((DIST-d)/DIST)*(d/DIST)*2; - r[q] += mul1*0.25*cells[Layer::MEATS][scx][scy]; //meat looks red - g[q] += mul1*0.25*cells[Layer::PLANTS][scx][scy]; //plants are green - r[q] += mul1*0.25*cells[Layer::FRUITS][scx][scy]; //fruit looks yellow - g[q] += mul1*0.25*cells[Layer::FRUITS][scx][scy]; - b[q] += mul1*0.25*cells[Layer::HAZARDS][scx][scy]; //hazards are blue??? - if(a->selectflag && isDebug()){ + float fov = a->eyefov[q] + conf::CZ/d; + + if (eye_target_angle < fov) { + float invDIST = INV_MAX_SENSORY_DISTANCE/conf::SMELL_DIST_MULT; + //we see the cell with this eye. Accumulate stats + float sight_mult= AMBIENT_LIGHT_PERCENT*cap(lightmult*((fov - eye_target_angle)/fov)*(1-d*d*invDIST*invDIST)); + + float seen_r = 0, seen_g = 0, seen_b = 0; + + seen_r += sight_mult*cells[Layer::MEATS][tcx][tcy]; //meat looks red + seen_g += sight_mult*cells[Layer::PLANTS][tcx][tcy]; //plants are green + seen_r += sight_mult*cells[Layer::FRUITS][tcx][tcy]; //fruit looks yellow + seen_g += sight_mult*cells[Layer::FRUITS][tcx][tcy]; + seen_r += sight_mult*cells[Layer::HAZARDS][tcx][tcy]; //hazards are purple + seen_b += sight_mult*cells[Layer::HAZARDS][tcx][tcy]; + seen_b += sight_mult*(cells[Layer::ELEVATION][tcx][tcy]id) && isDebug()){ linesA.push_back(a->pos); linesB.push_back(cellpos); } } +/* + float forwangle= a->angle; + float diff4= forwangle- angle; + if (fabs(forwangle)>M_PI) diff4= 2*M_PI- fabs(forwangle); + diff4= fabs(diff4); + if (diff4angle; - float diff4= forwangle- angle; - if (fabs(forwangle)>M_PI) diff4= 2*M_PI- fabs(forwangle); - diff4= fabs(diff4); - if (diff4smell_mod*dimmensions; + meat*= a->smell_mod*dimmensions; + hazard*= a->smell_mod*dimmensions; + water*= a->smell_mod*dimmensions; + + //---AGENT-AGENT SENSES---// for (int j=0; j<(int)agents.size(); j++) { //can still see and smell dead agents, so no health check here if (i==j) continue; Agent* a2= &agents[j]; - if (a->pos.xpos.x-DIST || a->pos.x>a2->pos.x+DIST - || a->pos.y>a2->pos.y+DIST || a->pos.ypos.y-DIST) continue; + if (a->pos.xpos.x-MAX_SENSORY_DISTANCE || a->pos.x>a2->pos.x+MAX_SENSORY_DISTANCE + || a->pos.y>a2->pos.y+MAX_SENSORY_DISTANCE || a->pos.ypos.y-MAX_SENSORY_DISTANCE) continue; float d= (a->pos-a2->pos).length(); - if (dsmell_mod; - //smell: adds up all agents inside DIST - smellsum+= 0.1*a->smell_mod; + //---HEARING---// + //adds up vocalization and other emissions from agents inside MAX_SENSORY_DISTANCE + if(!a2->isDead() || a2->volume > 0) { //can't hear dead unless they're still making a sound (grace period stuff, not ghostly... no ghosts here) + for (int q=0; q < NUMEARS; q++){ - //sound and hearing: adds up vocalization and other emissions from agents inside DIST - for (int q=0;qradius, 0); + v.rotate(a->angle + a->eardir[q]); - Vector2f v(a->radius, 0); - v.rotate(a->angle + a->eardir[q]); + Vector2f earpos = a->pos+ v; - Vector2f earpos= a->pos+ v; + float eardist = (earpos-a2->pos).length(); - float eardist= (earpos-a2->pos).length(); + for(int n=0; n < Hearing::TYPES; n++){ + float otone = 0, ovolume = 0; - for(int n=0;n<2;n++){ - float otone= 0, ovolume= 0; - if(n==0){ //if n=0, do agent vocals. - otone= a2->tone; - ovolume= a2->volume; - }else if(n==1){ //if n=1, do agent wheels - otone= 0.25; - ovolume= (max(fabs(a2->w1),fabs(a2->w2))); - } //future: if n=2, do agent intake sound + if (n == Hearing::VOICE){ + otone = a2->tone; + ovolume = a2->volume; + } else if (n == Hearing::MOVEMENT){ + otone = conf::WHEEL_TONE; + ovolume = conf::WHEEL_VOLUME*(max(fabs(a2->w1),fabs(a2->w2))); + } //future: do agent intake sound - //package up this sound source if user is watching - if(getSelectedID()==a->id){ - int volfact= (int)(a->hear_mod*(1-eardist*invDIST)*ovolume*100); - float finalfact= otone + volfact; - selectedSounds.push_back(finalfact); - } + //package up this sound source if user is watching + if(isAgentSelected(a->id)){ + int volfact = (int)(a->hear_mod*(1-eardist*invDIST)*ovolume*100); + float finalfact = otone + volfact; + selectedSounds.push_back(finalfact); + } + + if(otone >= a->hearhigh[q]) continue; + if(otone <= a->hearlow[q]) continue; + + if(isAgentSelected(a->id)){ + //modify the selected sound listing if its actually heard so that we can change the visual + selectedSounds[selectedSounds.size()-1] += 100; + } - if(otone>=a->hearhigh[q]) continue; - if(otone<=a->hearlow[q]) continue; - if(getSelectedID()==a->id){ - //modify the selected sound listing if its actually heard so that we can change the visual - selectedSounds[selectedSounds.size()-1]+= 100; + float tonemult = cap( min((a->hearhigh[q] - otone)*INV_SOUND_PITCH_RANGE, (otone - a->hearlow[q])*INV_SOUND_PITCH_RANGE) ); + //sounds within SOUND_PITCH_RANGE of a low or high hearing range edge get scaled down, by the distance to that edge + + hearsum[q] += a->hear_mod*(1-eardist*invDIST)*ovolume*tonemult; } - float tonemult= cap(min((a->hearhigh[q] - otone)/SOUNDPITCHRANGE,(otone - a->hearlow[q])/SOUNDPITCHRANGE)); - //sounds within SOUNDPITCHRANGE of a low or high hearing range edge get scaled down, by the distance to that edge - hearsum[q]+= a->hear_mod*(1-eardist*invDIST)*ovolume*tonemult; + if(a->isTiny()) break; //small agents only get one ear input, the rest are 0 } - - if(a->isTiny()) break; //small agents only get one ear input } - float ang= (a2->pos- a->pos).get_angle(); //current angle between bots, with origin at agent a + //current angle between bots, in the interval [-pi,pi] radians (a-> is at origin). Used for both Blood and Eyesight + float ang= (a2->pos - a->pos).get_angle(); + + //---AGENT-AGENT EYESIGHT---// + if(light!=0){//we will skip all eyesight if our agent is in the dark (light==0) + float lightmult = light*a->eye_see_agent_mod; - //eyes: bot sight - //we will skip all eyesight if our agent is in the dark (light==0) - if(light!=0){ for(int q=0;qisTiny() && !a->isTinyEye(q)){ //small agents have half-count of eyes, the rest just keep the ambient light values they got continue; - //careful about trying to do anything else in here; it depends on if the agent is within DIST of another } - float aa = a->angle + a->eyedir[q];//get eye's absolute (world) angle, with origin at agent a - if (aa<-M_PI) aa += 2*M_PI; - if (aa>M_PI) aa -= 2*M_PI;//not only loop aa angle, but throw it a whole circle around (we're allowing negative) + float eye_absolute_angle = a->angle + a->eyedir[q]; //get eye's absolute (world) angle + // a->angle is in [-pi,pi], a->eyedir[] is in [0,2pi]. it's possible to be > pi but not > 3*pi or < -pi + if (eye_absolute_angle >= M_PI) eye_absolute_angle -= 2*M_PI; //correct if > pi - float diff1= aa- ang; //get the difference in absolute angles between the eye and the other agent - if (fabs(diff1)>M_PI) diff1= 2*M_PI- fabs(diff1); //now take the absolute value to get a raw angle between - diff1= fabs(diff1); + //remember, eye_absolute_angle is in [-pi,pi], and ang is in [-pi,pi] + float eye_target_angle = eye_absolute_angle - ang; //get the difference in angles between the eye and the other agent + if (eye_target_angle >= M_PI) eye_target_angle -= 2*M_PI; //correct to within [-pi,pi] again, because a difference of 2pi is no difference at all + else if (eye_target_angle < -M_PI) eye_target_angle += 2*M_PI; + eye_target_angle = fabs(eye_target_angle); float fov = a->eyefov[q] + a2->radius/d; //add in radius/d to allowed fov, which for large distances is the same as the angle //"fov" here is a bit of a misnomer, as it's taking into account the seen agent's radius. This is used later in the calc again - - if (diff1eye_see_agent_mod*(fabs(fov-diff1)/fov)*(1-d*d*invDIST*invDIST); + float sight_mult= cap(lightmult*((fov - eye_target_angle)/fov)*(1-d_center_inv_DIST*d_center_inv_DIST)); - if(r[q]real_red) r[q]= mul1*a2->real_red; - if(g[q]real_gre) g[q]= mul1*a2->real_gre; - if(b[q]real_blu) b[q]= mul1*a2->real_blu; - if(a->id==SELECTION && isDebug()){ //debug sight lines, get coords + float seen_r = sight_mult*a2->real_red; + float seen_g = sight_mult*a2->real_gre; + float seen_b = sight_mult*a2->real_blu; + + if(r[q]id) && isDebug()){ //debug sight lines, get coords linesA.push_back(a->pos); linesB.push_back(a2->pos); } @@ -1230,38 +1423,47 @@ void World::setInputs() } } - //blood sensor - float forwangle= a->angle; - float diff4= forwangle- ang; - if (fabs(forwangle)>M_PI) diff4= 2*M_PI- fabs(forwangle); - diff4= fabs(diff4); - if (diff4blood_mod*(fabs(PI38-diff4)/PI38)*(1-d*invDIST)*(1-agents[j].health/conf::HEALTH_CAP); + //---BLOOD SENSE---// + float bloodangle = a->angle - ang; //remember, a->angle is in [-pi,pi], and ang is in [-pi,pi] + if (bloodangle >= M_PI) bloodangle -= 2*M_PI; //correct to within [-pi,pi] again, because a difference of 2pi is no difference at all + else if (bloodangle < -M_PI) bloodangle += 2*M_PI; + bloodangle= fabs(bloodangle); + + if (bloodangle < PI38) { + float newblood= a->blood_mod*((PI38 - bloodangle)/PI38)*(1-d_center_inv_DIST)*(1-agents[j].health/conf::HEALTH_CAP); if(newblood>blood) blood= newblood; //agents with high life dont bleed. low life makes them bleed more. dead agents bleed the maximum } } } - //temperature varies from 0 to 1 across screen vertically - float temp= calcTempAtCoord(a->pos.y); - + //---FINALIZE EYES---// for(int i=0;iin[Input::EYES+i*3]= cap(r[i]); a->in[Input::EYES+i*3+1]= cap(g[i]); a->in[Input::EYES+i*3+2]= cap(b[i]); } + //---HEALTH---// + a->in[Input::HEALTH]= cap(a->health/conf::HEALTH_CAP); //if we start using mutable health maximums, use that instead of HEALTH_CAP + + //---REPRODUCTION COUNTER---// + a->in[Input::REPCOUNT]= cap((a->maxrepcounter-a->repcounter)/a->maxrepcounter); //inverted repcounter, babytime is input val of 1 + + // = = = = = END SENSES COLLECTION = = = = = // + + //---PACKAGE UP INPUTS---// float t= (modcounter+current_epoch*FRAMES_PER_EPOCH); a->in[Input::CLOCK1]= abs(sinf(t/a->clockf1)); if(!a->isTiny()) a->in[Input::CLOCK2]= abs(sinf(t/a->clockf2)); if(!a->isTiny()) a->in[Input::CLOCK3]= abs(sinf(t/a->clockf3)); - a->in[Input::HEARING1]= cap(hearsum[0]); - a->in[Input::HEARING2]= cap(hearsum[1]); + for(int i=0;iin[Input::EARS+i]= cap(hearsum[i]); + } a->in[Input::BOT_SMELL]= cap(smellsum); a->in[Input::BLOOD]= cap(blood); - a->in[Input::TEMP]= temp; - if (a->id!=SELECTION) { + a->in[Input::TEMP]= calcTempAtCoord(a->pos.y); + if (!isAgentSelected(a->id)) { a->in[Input::PLAYER]= 0.0; } a->in[Input::FRUIT_SMELL]= cap(fruit); @@ -1270,58 +1472,128 @@ void World::setInputs() a->in[Input::WATER_SMELL]= cap(water); a->in[Input::RANDOM]= cap(randn(a->in[Input::RANDOM],0.08)); } - - #if defined(_DEBUG) - if(DEBUG) printf("/"); - #endif } void World::brainsTick() { #pragma omp parallel for schedule(dynamic) for (int i=0; i<(int)agents.size(); i++) { - if(agents[i].health<=0) continue; + if(agents[i].isDead()) continue; agents[i].tick(); } } -void World::processOutputs(bool prefire) +void World::processCounters() { - #if defined(_DEBUG) - if(DEBUG) printf("O"); - #endif + #pragma omp parallel for + for (int i=0; i<(int)agents.size(); i++) { + Agent* a= &agents[i]; + + //process indicator, used in drawing + if(a->indicator>0) a->indicator-= 0.5; + if(a->indicator<0) a->indicator-= 1; + + //process jaw renderer + if(a->jawrend>0 && a->jawPosition==0 || a->jawrend==conf::JAWRENDERTIME) a->jawrend-=1; + + //process carcass counter, which keeps dead agents on the world while meat is under them + if(a->carcasscount>=0) a->carcasscount++; + + //if alive... + if(!a->isDead()){ + //reduce fresh kill flag + if(a->freshkill>0) a->freshkill-= 1; + + //Live mutations + for(int m=0; mliveMutate(MUTEVENTMULT); + addTipEvent("Selected Agent Was Mutated!", EventColor::PURPLE, a->id); //control.cpp - want this to be supressed when in fast mode + STATlivemutations++; + if(DEBUG) printf("Live Mutation Event\n"); + } + } + + //Age goes up! + if (modcounter%(FRAMES_PER_DAY/10)==0) a->age+= 1; + + //update jaw + if(a->jawPosition>0) { + a->jawPosition*= -1; //jaw is an instantaneous source of damage. Reset to a negative number if positive + a->jawrend= conf::JAWRENDERTIME; //set render timer + + //once negative, jaw moves toward to 0, slowly if near -1, faster once closer to 0 + } else if (a->jawPosition<0) a->jawPosition= min(0.0, a->jawPosition + 0.01*(2 + a->jawPosition)); + + //update center render mode. Asexual pulls toward 0, female sexual pulls toward 1, male sexual towards 2 + if(a->isAsexual()) a->centerrender-= 0.01*(a->centerrender); + else if(a->isMale()) a->centerrender-= 0.01*(a->centerrender-2); + else a->centerrender-= 0.01*(a->centerrender-1); + a->centerrender= cap(a->centerrender*0.5)*2; //duck the counter under a cap to allow range [0,2] + } + } +} +void World::processOutputs(bool prefire) +// A prefire is needed when loading, as many values need refreshing (for example, from save loading), but the agents shouldn't move yet +{ #pragma omp parallel for schedule(dynamic) for (int i=0; i<(int)agents.size(); i++) { - Agent* a= &agents[i]; + Agent* a = &agents[i]; - if (a->health<=0) { - //dead agents continue to exist, come to a stop, and loose their voice, but skip everything else - float sp= conf::DEADSLOWDOWN*(a->w1+a->w2)*0.5; - a->w1= sp; - a->w2= sp; + if (a->isDead()) { + //dead agents continue to exist, slow down, and loose their voice, but skip everything else + float sp = conf::DEADSLOWDOWN*(a->w1+a->w2)*0.5; + a->w1 = sp; + a->w2 = sp; - a->volume*= 0.2; - if(a->volume<0.0001) a->volume= 0; + a->volume *= 0.2; + if(a->volume < 0.0001) a->volume = 0; - a->sexproject= 0; //no necrophilia - a->boost= false; //no boosting + a->sexproject = 0; //no necrophilia + a->boost = false; //no boosting continue; } + //set exhaustion + float boostmult = a->boost ? BOOST_EXAUSTION_MULT : 1; + float exhaustion_outputs = a->getOutputSum()*EXHAUSTION_MULT_PER_OUTPUT; + float exhaustion_conns = a->brain.conns.size()*EXHAUSTION_MULT_PER_CONN; + float exhaustion_brain = exhaustion_outputs*boostmult + exhaustion_conns; + + a->exhaustion = max((float)0, (a->exhaustion + exhaustion_brain) + BASEEXHAUSTION)*0.333333; // /3 to average the value + + //temp discomfort gets re-calculated intermittently based on size, to simulate heat absorption/release + if (modcounter%(int)ceil(10+2*a->radius) == 0 || prefire){ + //calculate temperature at the agents spot. (based on distance from horizontal equator) + float currenttemp = calcTempAtCoord(a->pos.y); + a->discomfort += 0.1*( (currenttemp-a->temperature_preference) - a->discomfort ); //if agent is too cold, value is negative + if (abs(a->discomfort) < conf::MIN_TEMP_DISCOMFORT_THRESHOLD) a->discomfort = 0; //below a minimum, agent can deal with it, no problem + } + + //return to processing outputs now float exh= 1.0/(1.0+a->exhaustion); //*exh reduces agent output->physical action rates for high exhaustion values + //jump gets set to 2*((jump output) - 0.5) if itself is zero (the bot is on the ground) and if jump output is greater than 0.5 + if(GRAVITY_ACCELERATION>0 && a->jump==0 && a->age>0){ + float height= (a->out[Output::JUMP]*exh - 0.5)*2; + if (height>0){ + a->jump= height; + if(!STATuserseenjumping) { + addTipEvent("Selected Agent jumped!", EventColor::YELLOW, a->id); + if (randf(0,1)<0.05) STATuserseenjumping= true; + } + } + } //do this before setting wheels + if (!a->isAirborne()) { //if not jumping, then change wheel speeds. otherwise, we want to keep wheel speeds constant - if (pcontrol && a->id==SELECTION) { + if (pcontrol && isAgentSelected(a->id)) { a->w1= pright; a->w2= pleft; } else { a->w1+= a->strength*(a->out[Output::LEFT_WHEEL_F] - a->out[Output::LEFT_WHEEL_B] - a->w1); a->w2+= a->strength*(a->out[Output::RIGHT_WHEEL_F] - a->out[Output::RIGHT_WHEEL_B] - a->w2); } - //apply exhaustion later, when moving agents -// a->w1*= exh; -// a->w2*= exh; } a->real_red+= 0.2*((1-a->chamovid)*a->gene_red + a->chamovid*a->out[Output::RED]-a->real_red); a->real_gre+= 0.2*((1-a->chamovid)*a->gene_gre + a->chamovid*a->out[Output::GRE]-a->real_gre); @@ -1343,20 +1615,14 @@ void World::processOutputs(bool prefire) a->grabbing= a->out[Output::GRAB]*exh; a->grabangle+= 0.2*exh*(a->out[Output::GRAB_ANGLE]*2*M_PI-a->grabangle); //exhaustion effects angle move speed - //jump gets set to 2*((jump output) - 0.5) if itself is zero (the bot is on the ground) and if jump output is greater than 0.5 - if(GRAVITYACCEL>0 && a->jump==0 && a->age>0){ - float height= (a->out[Output::JUMP]*exh - 0.5)*2; - if (height>0) a->jump= height; - } - //jaw *snap* mechanic float newjaw= cap(a->out[Output::JAW] - a->jawOldOutput)*exh; //new output has to be at least twice the old output to really trigger - if (a->jawPosition==0 && a->age>0 && newjaw>0.01) a->jawPosition= newjaw; + if (BITE_DISTANCE > 0 && a->jawPosition==0 && a->age>TENDERAGE && newjaw>0.01) a->jawPosition= newjaw; a->jawOldOutput= a->out[Output::JAW]; //clock 3 gets frequency set by output, but not immediately - a->clockf3+= 0.3*(95*(1-a->out[Output::CLOCKF3])+5 - a->clockf3); //exhaustion doesn't effect clock freq + a->clockf3+= 0.05*(95*(1-a->out[Output::CLOCKF3])+5 - a->clockf3); //exhaustion doesn't effect clock freq if(a->clockf3>100) a->clockf3= 100; if(a->clockf3<2) a->clockf3= 2; @@ -1367,11 +1633,11 @@ void World::processOutputs(bool prefire) //ceil(40.0/(a->tone+0.0000001)) makes it so that low tones make less often sounds, high tones make more often, fastest possible is near 40 ticks float playx= a->pos.x, playy= a->pos.y; - if(a->id==SELECTION) { playx= 0; playy= 0; } //if we're selected, forget position, set us right at the listener position + if(isAgentSelected(a->id)) { playx= 0; playy= 0; } //if we're selected, forget position, set us right at the listener position //Finally, these tones and volumes have been pre-adjusted for the sounds used in game: - if(a->isTiny()) tryPlayAudio(conf::SFX_CHIRP1, playx, playy, a->tone*0.8+0.3, a->volume*0.2); //tiny agents make a high-pitched chirp, like crickets - else tryPlayAudio(conf::SFX_CHIRP2, playx, playy, a->tone*0.8+0.2, a->volume*0.7); //large agents make a lower, cat-like "churr" + if(a->isTiny()) tryPlayAudio(conf::SFX_CHIRP1, playx, playy, a->tone*0.8+0.3, (a->volume+0.2)*0.5-0.1); //tiny agents make a high-pitched chirp, like crickets + else tryPlayAudio(conf::SFX_CHIRP2, playx, playy, a->tone*0.8+0.2, a->volume); //large agents make a lower, cat-like "churr" } //backup previous x & y pos's This is done as a prefire on purpose (it's not saved) @@ -1379,20 +1645,22 @@ void World::processOutputs(bool prefire) a->dpos.y= a->pos.y; } - //move bots if this is not a prefire. A prefire is needed when loading, as all the values above need refreshing, but we shouldn't move yet + //move bots if this is not a prefire if(!prefire){ float invMEANRADIUS= 1/MEANRADIUS; + #pragma omp parallel for schedule(dynamic) for (int i=0; i<(int)agents.size(); i++) { Agent* a= &agents[i]; - if(a->jump>-1) a->jump-= GRAVITYACCEL; //-1 because we will be nice and give a "recharge" time between jumps + if(a->jump>-1) a->jump-= GRAVITY_ACCELERATION; //-1 because we will be nice and give a "recharge" time between jumps else a->jump= 0; //first calculate the exact wheel scalar values float basewheel= WHEEL_SPEED; if (a->encumbered) basewheel*= conf::ENCUMBEREDMULT; - else if (a->boost) basewheel*= BOOSTSIZEMULT; + else if (a->boost) basewheel*= BOOST_MOVE_MULT; + else if (a->isAirborne()) basewheel*= JUMP_MOVE_BONUS_MULT; //only if boost isn't active basewheel*= sqrt(a->radius*invMEANRADIUS); //wheel speed depends on the size of the agent: smaller agents move slower basewheel/= (1.0+a->exhaustion); //apply exhaustion. Wheels are very sensitive @@ -1408,9 +1676,10 @@ void World::processOutputs(bool prefire) Vector2f vwheelright= -vleft; //the vectors get rotated by the amount (???) and then merged into position (???) + //I believe this was done to make large values have less impact than smaller ones, perhaps encouraging fine-tuning behavior vwheelleft.rotate(-BW1*M_PI); vwheelright.rotate(BW2*M_PI); - a->pos+= (vwheelleft - vleft)+(vwheelright + vleft); + a->pos+= (vwheelleft - vleft) + (vwheelright + vleft); //angle bots if (!a->isAirborne()) { @@ -1423,40 +1692,39 @@ void World::processOutputs(bool prefire) a->borderRectify(); } } - - #if defined(_DEBUG) - if(DEBUG) printf("/"); - #endif } -void World::processInteractions() +void World::processCellInteractions() { //process interaction with cells - #if defined(_DEBUG) - if(DEBUG) printf("CA"); - #endif - #pragma omp parallel for for (int i=0; i<(int)agents.size(); i++) { - if(agents[i].health<=0 && modcounter%25>0) continue; // okay, so what we're doing here is complicated - //if the agent is alive, we process them. If they are dead and the tick count mod 25 is not 0, we process them - //we want to process dead agents because we want to check if they have meat under them, and if so, reset the corpse counter Agent* a= &agents[i]; + if(a->isDead() && modcounter%25>0) continue; // okay, so what we're doing here is complicated + //if the agent is alive, we process them. If they are dead and the tick count mod 25 is 0, we process them + //we want to process dead agents because we want to check if they have meat under them, and if so, reset the corpse counter + float randr= min(a->radius, (float)abs(randn(0.0,a->radius/3))); Vector2f source(randr, 0); source.rotate(randf(-M_PI,M_PI)); - int scx= (int) capm(a->pos.x + source.x, 0, conf::WIDTH)/conf::CZ; - int scy= (int) capm(a->pos.y + source.y, 0, conf::HEIGHT)/conf::CZ; + int scx= (int) capm(a->pos.x + source.x, 0, conf::WIDTH-1)/conf::CZ; + int scy= (int) capm(a->pos.y + source.y, 0, conf::HEIGHT-1)/conf::CZ; + + #if defined(_DEBUG) + if(isDebug() && isAgentSelected(agents[i].id)) { + printf("scx: %i, scy: %i\n", scx, scy); + } + #endif - if(agents[i].health>0){ - if (a->jump<=0){ //no interaction with these cells if jumping + if(!a->isDead()){ + if (!a->isAirborne() && !a->boost){ //no intake if jumping or boosting float intake= 0; - float speedmult= pow(1-max(abs(a->w1), abs(a->w2)),3); //penalty for moving + float speedmult= pow(1 - max(abs(a->w1), abs(a->w2)),3); //penalty for moving speedmult/= (1.0+a->exhaustion); //exhaustion reduces agent physical actions including all intake - float invmult= 1-STOMACH_EFF; + float invmult= 1-STOMACH_EFFICIENCY; float invplant=1-a->stomach[Stomach::PLANT]*invmult; float invmeat= 1-a->stomach[Stomach::MEAT]*invmult; float invfruit=1-a->stomach[Stomach::FRUIT]*invmult; @@ -1466,75 +1734,91 @@ void World::processInteractions() //plant food float food= cells[Layer::PLANTS][scx][scy]; if (food>0) { //agent eats the food - //Plant intake is proportional to plant stomach, inverse to meat & fruit stomachs, and speed/exhaustion + //Plant intake is proportional to plant stomach, inverse to speed & exhaustion //min rate is the actual amount of food we can take. Otherwise, apply wasterate - float planttake= min(food,FOODWASTE*a->stomach[Stomach::PLANT]*invmeat*invfruit*speedmult); + float planttake= min(food, PLANT_WASTE*a->stomach[Stomach::PLANT]*invmeat*invfruit*speedmult); //unique for plant food: the less there is, the harder it is to eat, but not impossible - planttake*= max(food,0.25f); + planttake*= max(food,PLANT_TENACITY); //decrease cell content cells[Layer::PLANTS][scx][scy]-= planttake; - //now convert the taken food into intake for the agent - intake+= FOODINTAKE*planttake/FOODWASTE; - a->addIntake(conf::FOOD_TEXT, planttake); + //now convert the taken food into intake for the agent, applying inverted stomach mults + intake+= PLANT_INTAKE*planttake/PLANT_WASTE; + //this way, it's possible to eat a lot of something from the world, but if stomach isn't efficient, it's wasted + a->addIntake(conf::PLANT_TEXT, planttake); } //meat food float meat= cells[Layer::MEATS][scx][scy]; if (meat>0) { //agent eats meat - float meattake= min(meat,MEATWASTE*a->stomach[Stomach::MEAT]*invplant*invfruit*speedmult); + float meattake= min(meat,MEAT_WASTE*a->stomach[Stomach::MEAT]*invplant*invfruit*speedmult); cells[Layer::MEATS][scx][scy]-= meattake; - intake+= MEATINTAKE*meattake/MEATWASTE; + intake+= MEAT_INTAKE*meattake/MEAT_WASTE; a->addIntake(conf::MEAT_TEXT, meattake); } //Fruit food float fruit= cells[Layer::FRUITS][scx][scy]; if (fruit>0) { //agent eats fruit - float fruittake= min(fruit,FRUITWASTE*a->stomach[Stomach::FRUIT]*invmeat*invplant*cap(speedmult-0.5)*2); + float fruittake= min(fruit,FRUIT_WASTE*a->stomach[Stomach::FRUIT]*invmeat*invplant*cap(speedmult-0.5)*2); //unique for fruit - speed penalty is more extreme, being completely 0 until agents slow down <0.25 cells[Layer::FRUITS][scx][scy]-= fruittake; - intake+= FRUITINTAKE*fruittake/FRUITWASTE; + intake+= FRUIT_INTAKE*fruittake/FRUIT_WASTE; a->addIntake(conf::FRUIT_TEXT, fruittake); } // proportion intake via metabolism - a->repcounter -= a->metabolism*(1-MIN_INTAKE_HEALTH_RATIO)*intake; - if (a->repcounter<0) a->encumbered= true; - else a->encumbered= false; + float metabmult = a->metabolism*(1 - MIN_INTAKE_HEALTH_RATIO); + a->repcounter -= metabmult*intake; + if (a->repcounter < 0) a->encumbered = true; + else a->encumbered = false; + + a->health += (1 - metabmult)*intake; + //for default settings, metabolism splits intake this way + // M=0 M=0.5 M=1 + //H: 1 0.75 0.5 + //R: 0 0.25 0.5 + // Using a MIN_INTAKE_HEALTH_RATIO of 0, the above table becomes + // M=0 M=0.5 M=1 + //H: 1 0.5 0 + //R: 0 0.5 1 + // Using a ratio of 1, the table is always H: 1 and R: 0, no babies ever will be born, because all agents take 100% intake for health - a->health += (1-a->metabolism*(1-MIN_INTAKE_HEALTH_RATIO))*intake; //---END FOOD---// + //if (isAgentSelected(a->id)) printf("intake/sum(rates)= %f\n", 6*intake/(PLANT_INTAKE + FRUIT_INTAKE + MEAT_INTAKE)); + //hazards float hazard= cells[Layer::HAZARDS][scx][scy]; if (hazard>0){ //the young are spry, and don't take much damage from hazard float agemult= 1.0; - if(a->ageage0.9) damage*= HAZARDEVENT_MULT; //if a hazard event, apply multiplier + float damage= HAZARD_DAMAGE*agemult*pow(hazard, HAZARD_POWER); + if(hazard>0.9) { + damage*= HAZARD_EVENT_MULT; //if a hazard event, apply multiplier + if(modcounter%5==0) addTipEvent("Agent struck by lightning!", EventColor::PURPLE, a->id); + } a->addDamage(conf::DEATH_HAZARD, damage); } + } - //waste deposit - int freqwaste= (int)max(1.0f, a->out[Output::WASTE_RATE]*MAXWASTEFREQ); - //agents can control the rate of deposit, [1,MAXWASTEFREQ] frames - if (modcounter%freqwaste==0){ - //agents fill up hazard cells only up to 9/10, because any greater is a special event - if(hazard<0.9) hazard+= HAZARDDEPOSIT*freqwaste*a->health; - - cells[Layer::HAZARDS][scx][scy]= capm(hazard, 0, 0.9); - } + //waste deposit; done regardless of boost or jump + int freqwaste= (int)max(1.0f, a->out[Output::WASTE_RATE]*MAXWASTEFREQ); + //agents can control the rate of deposit, [1,MAXWASTEFREQ] frames + if (modcounter%freqwaste==0){ + float hazard= cells[Layer::HAZARDS][scx][scy]; + //agents fill up hazard cells only up to 9/10, because any greater is a special event + if(hazard<0.9) hazard+= HAZARD_DEPOSIT*freqwaste*a->health; + + cells[Layer::HAZARDS][scx][scy]= capm(hazard, 0, 0.9); } //land/water (always interacted with) float land= cells[Layer::ELEVATION][scx][scy]; if(a->isAirborne()){ - //aquatic jumpers get to breathe at their prefered lung value, IF the terrain they're in is deeper - //terrestrial jumpers get to breathe at 0.5 (beach) if they are in anything deeper //jumping while underwater allows one to breathe at their desired lung value if aquatic and too deep, or 0.5 if terrestrial, //meaning land creatures can "float" when they jump. Amphibians have best of both worlds land= max(land, min(Elevation::BEACH_MID, a->lungs)); @@ -1549,6 +1833,7 @@ void World::processInteractions() if(OVERHEAL_REPFILL!=0) a->repcounter -= (a->health-conf::HEALTH_CAP)*OVERHEAL_REPFILL; //if enabled, allow overflow to first fill the repcounter a->health= conf::HEALTH_CAP; } + } else { //agent is dead. Just check for meat and refresh corpse counter if meat amount is high enough float meat= cells[Layer::MEATS][scx][scy]; @@ -1556,98 +1841,121 @@ void World::processInteractions() } } +} - #if defined(_DEBUG) - if(DEBUG) printf("/AA"); //PLEASE note: this is the '/' end flag for CA, merged with AA, the next section. - //If you split this func, remember to split this! - #endif - +void World::processAgentInteractions() +{ //process agent-agent dynamics if (modcounter%2==0) { //we dont need to do this TOO often. can save efficiency here since this is n^2 op in #agents - //first, we'll determine for all agents if they are near enough to another agent to warrent processing them + //phase 1: determine for all agents if they are near enough to another agent to warrant processing them + #pragma omp parallel for schedule(dynamic) for (int i=0; i<(int)agents.size(); i++) { - agents[i].near= false; - agents[i].dhealth= 0; //better place for this now, since modcounter%2 - if (agents[i].health<=0) continue; //skip dead agents + Agent* a1= &agents[i]; + + a1->near= false; + a1->dhealth= 0; //no better place for this now, since we use it in phase 2 + if (a1->isDead()) continue; //skip dead agents for (int j=0; jagents[j].pos.x+NEAREST - || agents[i].pos.y>agents[j].pos.y+NEAREST - || agents[i].pos.ypos.xpos.x-NEAREST + || a1->pos.x>a2->pos.x+NEAREST + || a1->pos.y>a2->pos.y+NEAREST + || a1->pos.ypos.y-NEAREST) continue; + + if (a2->isDead()) { + if(a1->isGrabbing() && a1->grabID==a2->id) a1->grabID= -1; continue; } + + a1->near= true; + a2->near= true; } } + //phase 2: actually process agents which were flagged in phase 1 #pragma omp parallel for schedule(dynamic) for (int i=0; i<(int)agents.size(); i++) { - if(agents[i].health<=0) continue; + if(agents[i].isDead()) continue; if(!agents[i].near){ //no one is near enough this agent to process interactions. break grabbing and skip agents[i].grabID= -1; continue; } - Agent* a= &agents[i]; + Agent* a = &agents[i]; for (int j=0; j<(int)agents.size(); j++) { if (i==j || !agents[j].near) continue; - if(agents[j].health==0) continue; //health == because we want to weed out bots who died already via other causes + if(agents[j].health == 0) continue; //health == because we want to weed out bots who died already via other causes, but not ones in process of dying - Agent* a2= &agents[j]; + Agent* a2 = &agents[j]; - float d= (a->pos-a2->pos).length(); - float sumrad= a->radius+a2->radius; + float d = (a->pos-a2->pos).length(); + float sumrad = a->radius+a2->radius; + float ainvrad = 1/a->radius; + float a2invrad = 1/a2->radius; //---HEALTH GIVING---// - if (FOOD_SHARING_DISTANCE>0 && FOODTRANSFER!=0 && !a->isSelfish(conf::MAXSELFISH) && a2->health+FOODTRANSFER<2) { + + if (FOOD_SHARING_DISTANCE>0 && GENEROCITY_RATE!=0 && !a->isSelfish(conf::MAXSELFISH) && a2->health+GENEROCITY_RATE<2) { //all non-selfish agents allow health trading to anyone within their kin range - float deviation= abs(a->species - a2->species); + float deviation = abs(a->species - a2->species); - float rd= a->isGiving() ? FOOD_SHARING_DISTANCE : sumrad+1; + float rd = a->isGiving() ? FOOD_SHARING_DISTANCE : sumrad+1; //rd is the max range allowed to agent j. If generous, range allowed, otherwise bots must basically touch - if (d<=rd && deviationkinrange) { + if (d <= rd && deviation <= a->kinrange) { + float healthrate = a->isGiving() ? GENEROCITY_RATE*a->give : GENEROCITY_RATE*2*a->give; + //healthrate goes from 0->1 for give [0,0.5], and from 0.5->1 for give (0.5,1]... + if(d <= sumrad + 1 && a->isGiving()) healthrate = GENEROCITY_RATE; + //...it is maxxed when touching and generous... + if(a->give != 1 && AGENTS_DONT_OVERDONATE) healthrate = capm(healthrate, 0, cap(a->health - a2->health)/2); + //...and can't give more than half the diff in health if agent is max giving (if they are, it is possible to give till they die) + //initiate transfer - float healthrate= a->isGiving() ? FOODTRANSFER*a->give : FOODTRANSFER*2*a->give; - if(d<=sumrad+1 && a->isGiving()) healthrate= FOODTRANSFER; - //healthrate goes from 0->1 for give [0,0.5], and from 0.5->1 for give (0.5,1]. Is maxxed when touching and generous a2->health += healthrate; - a->addDamage(conf::DEATH_GENEROUSITY, healthrate); + a->addDamage(conf::DEATH_GENEROSITY, healthrate); a2->dhealth += healthrate; //only for drawing a->dhealth -= healthrate; + + //play a soft purring sound for the agent being given to + float purrx= a2->pos.x, purry= a2->pos.y; + if(isAgentSelected(a2->id)) { purrx= 0; purry= 0; } + if((modcounter + a2->id + (int)a2->pos.x)%60==0) tryPlayAudio(conf::SFX_PURR1, purrx, purry, randf(0.8,1.1)); + + if(!STATuserseengenerosity && healthrate != 0){ + addTipEvent("Agent donated health!", EventColor::GREEN, a->id); + addTipEvent("Agent received health donation!", EventColor::GREEN, a2->id); + if (randf(0,1)<0.05) STATuserseengenerosity = true; + } } } //---COLLISIONS---/// - if (BUMP_PRESSURE>0 && disAirborne() && !a2->isAirborne()) { + + if (BUMP_PRESSURE > 0 && d < sumrad && !a->isAirborne() && !a2->isAirborne()) { //if inside each others radii and neither are jumping, fix physics float ov= (sumrad-d); - if (ov>0 && d>0.00001) { - if (ov>TOOCLOSE && TOOCLOSE>0) {//if bots are too close, they get injured before being pushed away - float aagemult= 1; - float a2agemult= 1; - if(a->ageage/TENDERAGE; - if(a2->ageage/TENDERAGE; - float DMG1= ov*DAMAGE_COLLIDE*sumrad/2/a->radius*aagemult; //larger, younger bots take less damage, bounce less - float DMG2= ov*DAMAGE_COLLIDE*sumrad/2/a2->radius*a2agemult; - - if(DEBUG) printf("\na collision occured. Damage on agent a: %.4f. Damage on agent a2: %.4f\n", DMG1, DMG2); + if (ov > 0 && d > 0.00001) { + if (BUMP_DAMAGE_OVERLAP > 0 && ov > BUMP_DAMAGE_OVERLAP) {//if bots are too close, they get injured before being pushed away + float invsumrad = 1/sumrad; + float aagemult = 1; + float a2agemult = 1; + if(a->age < TENDERAGE) aagemult = (float)(a->age+1)*INV_TENDERAGE; + if(a2->age < TENDERAGE) a2agemult = (float)(a2->age+1)*INV_TENDERAGE; + float damagemult = ov*DAMAGE_COLLIDE*MEANRADIUS/sumrad; + + float DMG1 = capm(damagemult*ainvrad*aagemult, 0, conf::HEALTH_CAP); //larger, younger bots take less damage, bounce less + float DMG2 = capm(damagemult*a2invrad*a2agemult, 0, conf::HEALTH_CAP); + + if(DEBUG) printf("\na collision occured. overlap: %.4f, aagemult: %.2f, a2agemult: %.2f, Damage on a: %f. Damage on a2: %f\n", ov, aagemult, a2agemult, DMG1, DMG2); a->addDamage(conf::DEATH_COLLIDE, DMG1); a2->addDamage(conf::DEATH_COLLIDE, DMG2); if(a->health==0){ - break; continue; } if(a2->health==0){ @@ -1656,18 +1964,25 @@ void World::processInteractions() } //only trigger collision splash if another splash not being displayed - if(a->indicator<=0) a->initSplash(conf::RENDER_MAXSPLASHSIZE*0.5,0,0.5,1); - if(a2->indicator<=0) a2->initSplash(conf::RENDER_MAXSPLASHSIZE*0.5,0,0.5,1); + if(a->indicator <= 0) a->initSplash(conf::RENDER_MAXSPLASHSIZE*0.5,0,0.5,1); + if(a2->indicator <= 0) a2->initSplash(conf::RENDER_MAXSPLASHSIZE*0.5,0,0.5,1); + addTipEvent("Agent collided hard", EventColor::CYAN, a->id); + addTipEvent("Agent collided hard", EventColor::CYAN, a2->id); - a->freshkill= FRESHKILLTIME; //this agent was hit this turn, giving full meat value - a2->freshkill= FRESHKILLTIME; + a->freshkill = FRESHKILLTIME; //this agent was hit this turn, giving full meat value + a2->freshkill = FRESHKILLTIME; } - float ff1= capm(ov/d*a2->radius/a->radius*BUMP_PRESSURE, 0, 2); //the radii come in here for inertia-like effect - float ff2= capm(ov/d*a->radius/a2->radius*BUMP_PRESSURE, 0, 2); - a->pos.x-= (a2->pos.x-a->pos.x)*ff1; - a->pos.y-= (a2->pos.y-a->pos.y)*ff1; - a2->pos.x+= (a2->pos.x-a->pos.x)*ff2; - a2->pos.y+= (a2->pos.y-a->pos.y)*ff2; + + float bumpmult = ov*BUMP_PRESSURE/d; + float ff1 = capm(bumpmult*a2->radius*ainvrad, 0, 2); //the radii come in here for inertia-like effect + float ff2 = capm(bumpmult*a->radius*a2invrad, 0, 2); + float diffx = (a2->pos.x-a->pos.x); + float diffy = (a2->pos.y-a->pos.y); + + a->pos.x -= diffx*ff1; + a->pos.y -= diffy*ff1; + a2->pos.x += diffx*ff2; + a2->pos.y += diffy*ff2; a->borderRectify(); a2->borderRectify(); @@ -1677,8 +1992,9 @@ void World::processInteractions() } //end collision mechanics //---SPIKING---// + //small spike doesn't count. If the target is jumping in midair, can't attack them either - if(a->isSpikey(SPIKELENGTH) && d<=(a2->radius + SPIKELENGTH*a->spikeLength) && !a2->isAirborne()){ + if(a->isSpikey(SPIKE_LENGTH) && d<=(a2->radius + SPIKE_LENGTH*a->spikeLength) && !a2->isAirborne()){ Vector2f v(1,0); v.rotate(a->angle); float diff= v.angle_between(a2->pos-a->pos); @@ -1703,7 +2019,7 @@ void World::processInteractions() if(DEBUG) printf("\nan agent received spike damage: %.4f\n", DMG); a2->addDamage(conf::DEATH_SPIKE, DMG); a2->freshkill= FRESHKILLTIME; //this agent was hit this turn, giving full meat value - if(a2->id==SELECTION) addEvent("Selected Agent was Stabbed!", EventColor::BLOOD); + addTipEvent("Agent was Stabbed!", EventColor::BLOOD, a2->id); a->hits++; a->spikeLength= 0; //retract spike down @@ -1713,10 +2029,10 @@ void World::processInteractions() if (a2->health==0){ //red splash means bot has killed the other bot. Murderer! a->initSplash(conf::RENDER_MAXSPLASHSIZE,1,0,0); - if(a->id==SELECTION) addEvent("Selected Agent Killed Another!", EventColor::RED); + addTipEvent("Agent Killed Another!", EventColor::RED, a->id); a->killed++; continue; - } else if(a->id==SELECTION) addEvent("Selected Agent Stabbed Another!", EventColor::ORANGE); + } else addTipEvent("Agent Stabbed Another!", EventColor::ORANGE, a->id); /*Vector2f v2(1,0); v2.rotate(a2->angle); @@ -1732,35 +2048,42 @@ void World::processInteractions() } //end spike mechanics //---JAWS---// - if(a->jawPosition>0 && d<=(sumrad+12.0)) { //only bots that are almost touching may chomp + + if(BITE_DISTANCE > 0 && a->jawPosition>0 && d <= (sumrad+BITE_DISTANCE)) { //only bots that are almost touching may chomp Vector2f v(1,0); v.rotate(a->angle); - float diff= v.angle_between(a2->pos-a->pos); + float diff= v.angle_between(a2->pos - a->pos); if (fabs(diff)jawPosition*(a->radius/a2->radius); //advantage over spike: large agents do more damage to smaller agents + float DMG= DAMAGE_JAWSNAP*a->jawPosition*(a->radius*a2invrad); //advantage over spike: large agents do more damage to smaller agents if(DEBUG) printf("\nan agent received bite damage: %.4f\n", DMG); a2->addDamage(conf::DEATH_BITE, DMG); a2->freshkill= FRESHKILLTIME; - if(a2->id==SELECTION) addEvent("Selected Agent was Bitten!", EventColor::BLOOD); + addTipEvent("Agent was Bitten!", EventColor::BLOOD, a2->id); a->hits++; a->initSplash(conf::RENDER_MAXSPLASHSIZE*0.5*DMG,1,1,0); //yellow splash means bot has chomped the other bot. ouch! + float chompvolume = 0.5; if (a2->health==0){ + chompvolume = 1; //red splash means bot has killed the other bot. Murderer! a->initSplash(conf::RENDER_MAXSPLASHSIZE,1,0,0); - if(a->id==SELECTION) addEvent("Selected Agent Killed Another!", EventColor::RED); + addTipEvent("Agent Killed Another!", EventColor::RED, a->id); a->killed++; if(a->grabID==a2->id) a->grabID= -1; continue; - } else if(a->id==SELECTION) addEvent("Selected Agent Bit Another!", EventColor::ORANGE); + } else addTipEvent("Agent Bit Another!", EventColor::YELLOW, a->id); + + if(randf(0,1)>0.5) tryPlayAudio(conf::SFX_CHOMP1, a->pos.x, a->pos.y, randn(1.0,0.2), chompvolume); + else tryPlayAudio(conf::SFX_CHOMP2, a->pos.x, a->pos.y, randn(1.0,0.2), chompvolume); } } //end jaw mechanics //---GRABBING---// + //doing this last because agent deaths may occur up till now, and we don't want to worry about holding onto things that die while holding them - if(GRAB_PRESSURE!=0 && GRABBING_DISTANCE>0 && a->isGrabbing() && a2->health>0) { + if(GRAB_PRESSURE!=0 && GRABBING_DISTANCE>0 && a->isGrabbing() && !a2->isDead()) { if(d<=GRABBING_DISTANCE+a->radius){ Vector2f v(1,0); v.rotate(a->angle+a->grabangle); @@ -1770,6 +2093,8 @@ void World::processInteractions() if(a->grabID==-1){ if (fabs(diff)grabID= a2->id; + addTipEvent("Agent grabbed another", EventColor::CYAN, a->id); + addTipEvent("Agent was grabbed", EventColor::CYAN, a2->id); } } @@ -1784,79 +2109,194 @@ void World::processInteractions() float dpref= sumrad + 1.5; Vector2f tpos(a->pos.x+dpref*cos(a->angle+a->grabangle),a->pos.y+dpref*sin(a->angle+a->grabangle)); - float ff1= a2->radius/a->radius*a->grabbing*GRAB_PRESSURE; //the radii come in here for inertia-like effect - float ff2= a->radius/a2->radius*a->grabbing*GRAB_PRESSURE; - a->pos.x-= (tpos.x-a2->pos.x)*ff1; - a->pos.y-= (tpos.y-a2->pos.y)*ff1; - a2->pos.x+= (tpos.x-a2->pos.x)*ff2; - a2->pos.y+= (tpos.y-a2->pos.y)*ff2; + float ff1= a2->radius*ainvrad*a->grabbing*GRAB_PRESSURE; //the radii come in here for inertia-like effect + float ff2= a->radius*a2invrad*a->grabbing*GRAB_PRESSURE; + a->pos.x-= (tpos.x-a2->pos.x)*ff1; + a->pos.y-= (tpos.y-a2->pos.y)*ff1; + a2->pos.x+= (tpos.x-a2->pos.x)*ff2; + a2->pos.y+= (tpos.y-a2->pos.y)*ff2; + + a->borderRectify(); + a2->borderRectify(); + } + + //the graber gets rotated toward the grabbed agent by a ratio of their radii, limited to 0.08 radian + a->angle+= capm(a->grabbing*diff*a2->radius*ainvrad, -0.08, 0.08); + } + } else if (a->grabID==a2->id) { + //grab distance exceeded, break the bond + a->grabID= -1; + } + } else { + //if we can't grab, or the other agent died, clear the grab target + a->grabID= -1; + } //end grab mechanics. DO NOT ADD DEATH CAUSES AFTER THIS + } + } + } +} + +void World::healthTick() +{ + #pragma omp parallel for + for (int i=0; i<(int)agents.size(); i++) { + Agent* a= &agents[i]; + if (!a->isDead()){ + //"natural" causes of death: age, wheel activity, excesive brain activity + //baseloss starts by penalizing high average wheel speed + float baseloss= HEALTHLOSS_WHEELS * (fabs(a->w1) + fabs(a->w2))/2; + + //getting older reduces health. + baseloss += HEALTHLOSS_AGING * (float)a->age / MAXAGE; + + //being exhausted reduces health (both indirectly through it's effects on output, and directly here so we can increase agent turnover) + baseloss += HEALTHLOSS_EXHAUSTION * a->exhaustion * a->exhaustion; + + //if boosting, loss from age, wheels, and exhaustion is multiplied + if (a->boost) { + baseloss *= HEALTHLOSS_BOOSTMULT; + } + + //brain activity reduces health (note: this doesn't make sense now that exhaustion is in the picture. default mult has been 0) + baseloss += HEALTHLOSS_BRAINUSE*a->getActivity(); + + a->addDamage(conf::DEATH_NATURAL, baseloss); + if (a->health==0) continue; //agent died, we must move on. + + //remove health from lack of "air", a straight up penalty to all bots for large population + a->addDamage(conf::DEATH_TOOMANYAGENTS, HEALTHLOSS_NOOXYGEN*(float)max((int)agents.size()-AGENTS_MIN_NOTCLOSED, 0)/AGENTS_MAX_NOOXYGEN); + if (a->health==0) continue; //agent died, we must move on! + + //apply temperature discomfort + a->addDamage(conf::DEATH_BADTEMP, HEALTHLOSS_BADTEMP*abs(a->discomfort)); + } + } +} + +void World::processReproduction() +{ + //the reasoning for this seems to be that if we process every tick, it opens up weird memory reference inaccessible issues, so we mod it + if(modcounter%2==0) { + for (int mother=0; mother<(int)agents.size(); mother++) { + if (agents[mother].repcounter<0 && agents[mother].health>=MINMOMHEALTH) { + //agent is healthy and is ready to reproduce. Now to decide how... + + if(SEXTING_DISTANCE>0 && !agents[mother].isAsexual()){ + if(agents[mother].isMale()) continue;; //'fathers' cannot themselves reproduce, only be reproduced with + + for (int father=0; father<(int)agents.size(); father++) { + if (mother == father) continue; + + if (agents[father].isAsexual() || agents[father].repcounter>0) continue; - a->borderRectify(); - a2->borderRectify(); - } + float deviation= abs(agents[mother].species - agents[father].species); //species deviation check + if (deviation>=agents[mother].kinrange) continue; //uses mother's kinrange; if outside, skip - //the graber gets rotated toward the grabbed agent by a ratio of their radii, limited to 0.08 radian - a->angle+= capm(a->grabbing*diff*a2->radius/a->radius, -0.08, 0.08); - } - } else if (a->grabID==a2->id) { - //grab distance exceeded, break the bond - a->grabID= -1; + float distance= (agents[mother].pos - agents[father].pos).length(); + if(distance>SEXTING_DISTANCE) continue; + + //prepare to add agents[i].numbabies to world, with two parents + if(DEBUG) printf("/nAttempting to have children..."); + + reproduce(mother, father); + + addTipEvent("Agent Sexually Reproduced", EventColor::BLUE, agents[mother].id); + addTipEvent("Agent Sexually Reproduced", EventColor::BLUE, agents[father].id); + tryPlayAudio(conf::SFX_SMOOCH1, agents[mother].pos.x, agents[mother].pos.y, randn(1.0,0.15)); + + if(DEBUG) printf(" Success!\n"); } } else { - //if we can't grab, or the other agent died, clear the grab target - a->grabID= -1; - } //end grab mechanics. DO NOT ADD DEATH CAUSES AFTER THIS + //this adds mother's numbabies to world, but with just one parent (budding) + if(DEBUG) printf("/nAttempting budding..."); + + reproduce(mother, mother); + + addTipEvent("Agent Assexually Budded", EventColor::NEON, agents[mother].id); + tryPlayAudio(conf::SFX_PLOP1, agents[mother].pos.x, agents[mother].pos.y, randn(1.0,0.15)); + + if(DEBUG) printf(" Success!\n"); + } + } + if(FUN && randf(0,1)<0.3){ + if(agents[mother].indicator<=0) agents[mother].indicator= 60; + agents[mother].ir= randf(0,1); + agents[mother].ig= randf(0,1); + agents[mother].ib= randf(0,1); } } } - - #if defined(_DEBUG) - if(DEBUG) printf("/"); - #endif } -void World::healthTick() +void World::processDeath() { - #if defined(_DEBUG) - if(DEBUG) printf("H"); - #endif - - #pragma omp parallel for schedule(dynamic) for (int i=0; i<(int)agents.size(); i++) { - if (agents[i].health>0){ - //"natural" causes of death: age, wheel activity, excesive brain activity - //baseloss starts by penalizing high average wheel speed - float baseloss= HEALTHLOSS_WHEELS*(fabs(agents[i].w1) + fabs(agents[i].w2))/2; + Agent* a = &agents[i]; + if(a->health < 0) { + printf("Please check the code: an agent unexpectedly had negative health when it should have had exactly zero\n"); + a->health = 0; + } + if (a->health == 0 && a->carcasscount == 0) { + //world is only now picking up the fact that the agent died from the agent itself + if (isAgentSelected(a->id)){ + printf("The Selected Agent was %s!\n", a->death.c_str()); + addTipEvent("The Selected Agent Died!", 0, a->id); - //getting older reduces health. - baseloss += (float)agents[i].age/MAXAGE*HEALTHLOSS_AGING; + //play audio clip of death. At position 0,0 (no position) if we had the agent selected, otherwise, use world coords + tryPlayAudio(conf::SFX_DEATH1, 0, 0, randn(1.0,0.05)); + } else tryPlayAudio(conf::SFX_DEATH1, a->pos.x, a->pos.y, randn(1.0,0.15)); - //if boosting, init (baseloss + age loss) * metab loss is multiplied by boost loss - if (agents[i].boost) { - baseloss *= HEALTHLOSS_BOOSTMULT; - } + //next, distribute meat + int cx = (int) a->pos.x/conf::CZ; + int cy = (int) a->pos.y/conf::CZ; - //brain activity reduces health - if(HEALTHLOSS_BRAINUSE!=0) baseloss += HEALTHLOSS_BRAINUSE*agents[i].getActivity(); + float meat = cells[Layer::MEATS][cx][cy]; + float agemult = a->ageage+1)*INV_TENDERAGE : 1.0; //young killed agents should give very little resources until age 9 + float freshmult = a->freshkill>0 ? 1.0 : MEAT_NON_FRESHKILL_MULT; //agents which were spiked recently will give full meat, otherwise give MEAT_NON_FRESHKILL_MULT + float stomachmult = 1+(conf::CARNIVORE_MEAT_EFF-1)*sqrt(a->stomach[Stomach::MEAT]); //carnivores give less, specified by CARNIVORE_MEAT_EFF - agents[i].addDamage(conf::DEATH_NATURAL, baseloss); - if (agents[i].health==0) continue; //agent died, we must move on. + meat += MEAT_VALUE*agemult*freshmult*stomachmult; + cells[Layer::MEATS][cx][cy] = cap(meat); - //remove health from lack of "air", a straight up penalty to all bots for large population - agents[i].addDamage(conf::DEATH_TOOMANYAGENTS, HEALTHLOSS_NOOXYGEN*agents.size()/AGENTS_MAX_NOOXYGEN); - if (agents[i].health==0) continue; //agent died, we must move on! + //collect all the death causes from all dead agents + deaths.push_back(a->death); + } + } - //apply temperature discomfort - agents[i].addDamage(conf::DEATH_BADTEMP, HEALTHLOSS_BADTEMP*agents[i].discomfort); + vector::iterator iter= agents.begin(); + while (iter != agents.end()) { + if (iter->isDead() && iter->carcasscount >= conf::CORPSE_FRAMES) { + if(isAgentSelected(iter->id) && isDemo()) addEvent("The Selected Agent decayed", EventColor::BROWN); + iter= agents.erase(iter); + } else { + ++iter; } } +} - #if defined(_DEBUG) - if(DEBUG) printf("/"); - #endif +void World::processRandomSpawn() +{ + if (!CLOSED) { + int alive= agents.size()-getDead(); + //make sure environment is always populated with at least AGENTS_MIN_NOTCLOSED bots + if (alive0 && agents[i].age>maxage) { + if (!agents[i].isDead() && agents[i].age>maxage) { maxage= agents[i].age; maxi= i; } @@ -1879,7 +2319,7 @@ void World::setSelection(int type) //highest generation: same as with Oldest, but this time the generation counter int maxgen= 0; for (int i=0; i<(int)agents.size(); i++) { - if (agents[i].health>0 && agents[i].gencount>maxgen) { + if (!agents[i].isDead() && agents[i].gencount>maxgen) { maxgen= agents[i].gencount; maxi= i; } @@ -1897,7 +2337,7 @@ void World::setSelection(int type) //Energetic: finds agent with most energy (lowest exhaustion value) float minexhaust= 1000; for (int i=0; i<(int)agents.size(); i++) { - if (agents[i].health>0 && agents[i].age>0 && agents[i].exhaustion0 && agents[i].exhaustion0 && agents[i].age!=0){ + if(!agents[i].isDead() && agents[i].age!=0){ float prog= (float)agents[i].children/sqrt((float)agents[i].age); //note: productivity needs to be / age b/c otherwise the oldest will almost always be the most productive //and we're using sqrt because pow^1 it would be inverse (youngest who just had babies is always selected) @@ -1922,16 +2362,16 @@ void World::setSelection(int type) float maxindex= 0; for (int i=0; i<(int)agents.size(); i++) { float index= 0.25*agents[i].hits + agents[i].killed; - if (agents[i].health>0 && index>maxindex) { + if (!agents[i].isDead() && index>maxindex) { maxindex= index; maxi= i; } } } else if (type==Select::RELATIVE){ //Relative: this special mode will calculate the closest relative to the selected if the selected died. Good mode to leave on fast mode - int select= getSelectedAgent(); + int select= getSelectedAgentIndex(); if(select>=0){ - if(agents[select].health<=0){ + if(agents[select].isDead()){ int index= getClosestRelative(select); if(index>=0) { maxi= index; @@ -1943,7 +2383,7 @@ void World::setSelection(int type) float maxindex= -1; for (int i=0; i<(int)agents.size(); i++) { float index= agents[i].stomach[Stomach::PLANT] - 0.5*agents[i].stomach[Stomach::MEAT] - 0.5*agents[i].stomach[Stomach::FRUIT]; - if (agents[i].health>0 && agents[i].gencount>0 && index>maxindex) { + if (!agents[i].isDead() && agents[i].gencount>0 && index>maxindex) { maxindex= index; maxi= i; } @@ -1953,7 +2393,7 @@ void World::setSelection(int type) float maxindex= -1; for (int i=0; i<(int)agents.size(); i++) { float index= agents[i].stomach[Stomach::FRUIT] - 0.5*agents[i].stomach[Stomach::MEAT] - 0.5*agents[i].stomach[Stomach::PLANT]; - if (agents[i].health>0 && agents[i].gencount>0 && index>maxindex) { + if (!agents[i].isDead() && agents[i].gencount>0 && index>maxindex) { maxindex= index; maxi= i; } @@ -1963,7 +2403,7 @@ void World::setSelection(int type) float maxindex= -1; for (int i=0; i<(int)agents.size(); i++) { float index= agents[i].stomach[Stomach::MEAT] - 0.5*agents[i].stomach[Stomach::FRUIT] - 0.5*agents[i].stomach[Stomach::PLANT]; - if (agents[i].health>0 && agents[i].gencount>0 && index>maxindex) { + if (!agents[i].isDead() && agents[i].gencount>0 && index>maxindex) { maxindex= index; maxi= i; } @@ -1971,7 +2411,7 @@ void World::setSelection(int type) } if (maxi!=-1) { - if(agents[maxi].id!= SELECTION) { + if(!isAgentSelected(agents[maxi].id)) { setSelectedAgent(maxi); if(type==Select::RELATIVE) tryPlayAudio(conf::SFX_UI_RELATIVESELECT); //else PLAY SIMPLE WOOSH SFX @@ -1986,19 +2426,19 @@ bool World::setSelectionRelative(int posneg) //Control.cpp??? { //returns bool because GLView would like to know if successful or not - int sid= getSelectedAgent(); + int sid= getSelectedAgentIndex(); if(sid>=0){ //get selected species id int species= agents[sid].species; int bestidx= sid; - int bestdd= agents[sid].kinrange; + int bestdd= agents[sid].kinrange + conf::VISUALIZE_RELATED_RANGE; //for each agent, check if its alive, not exactly like us, within kin range, and a positive difference in speciesID for(int i=0; iagents[sid].kinrange || dd<0) continue; + if(dd>=bestdd || dd<0) continue; if(dd0) agents[idx].initSplash(10,1.0,1.0,1.0); + if(!agents[idx].isDead()) agents[idx].initSplash(10,1.0,1.0,1.0); setControl(false); } -int World::getSelectedAgent() const +Agent* World::getSelectedAgent() +//Control.cpp +{ + int idx= -1; + #pragma omp parallel for + for (int i=0; i<(int)agents.size(); i++) { + if(isAgentSelected(agents[i].id)) idx= i; + } + if (idx>=0) { + return &agents[idx]; + } + return NULL; +} + +int World::getSelectedAgentIndex() //Control.cpp??? { //retrieve array index of selected agent, returns -1 if none int idx= -1; #pragma omp parallel for for (int i=0; i<(int)agents.size(); i++) { - if(agents[i].id==SELECTION) idx= i; + if(isAgentSelected(agents[i].id)) idx= i; } return idx; } @@ -2048,7 +2504,7 @@ int World::getClosestRelative(int idx) for (int i=0; i<(int)agents.size(); i++) { if(idx==i) continue; - if(abs(agents[idx].species-agents[i].species)0){ + if(!agents[i].isDead() && abs(agents[idx].species - agents[i].species) < agents[i].kinrange){ //choose an alive agent that is within kin range; closer the better meta= 1+agents[i].kinrange-abs(agents[idx].species-agents[i].species); } else continue; //if you aren't related, you're off to a very bad start if we're trying to find relatives... skip! @@ -2057,15 +2513,15 @@ int World::getClosestRelative(int idx) if(agents[idx].gencount+1==agents[i].gencount) { meta+= 3; bestrelative= "youngling"; } //choose children or nephews and nieces else if(agents[idx].gencount==agents[i].gencount) { meta+= 2; bestrelative= "cousin"; } //at least choose cousins... else if(agents[idx].gencount==agents[i].gencount+1) { meta+= 1; bestrelative= "elder"; } //...or parents/aunts/uncles - if(agents[idx].parentid==agents[i].parentid) { meta+= 10; bestrelative= "sibling"; } //...note this gets +12 b/c of cousin above - if(agents[idx].parentid==agents[i].id) { meta+= 16; bestrelative= "mother"; } //...note this gets +17 b/c of elder above - if(agents[idx].id==agents[i].parentid) { meta+= 17; bestrelative= "daughter"; } //...note this gets +20 b/c of youngling above + if(agents[idx].parentid==agents[i].parentid) { meta+= 100; bestrelative= "sibling"; } //...note this gets +102 b/c of cousin above + if(agents[idx].parentid==agents[i].id) { meta+= 100; bestrelative= "mother"; } //...note this gets +101 b/c of elder above + if(agents[idx].id==agents[i].parentid) { meta+= 100; bestrelative= "daughter"; } //...note this gets +103 b/c of youngling above for(int j=0; jSELECTION return SELECTION; } +bool World::isAgentSelected(int id) +{ + return id == SELECTION; +} -void World::selectedHeal() { +void World::selectedHeal() +//Control.cpp +{ //heal selected agent - int sidx= getSelectedAgent(); + int sidx= getSelectedAgentIndex(); if(sidx>=0){ agents[sidx].health= conf::HEALTH_CAP; agents[sidx].carcasscount= -1; @@ -2107,16 +2571,17 @@ void World::selectedHeal() { void World::selectedKill() { //kill (delete) selected agent - int sidx= getSelectedAgent(); + int sidx= getSelectedAgentIndex(); if(sidx>=0){ - if(agents[sidx].health<=0) agents[sidx].carcasscount= conf::CORPSE_FRAMES; + //if we press the button on an agent already dead, mark them for clearing them from the world + if(agents[sidx].isDead()) agents[sidx].carcasscount= conf::CORPSE_FRAMES; else agents[sidx].addDamage(conf::DEATH_USER, 9001); } } void World::selectedBabys() { //force selected agent to assexually reproduce - int sidx= getSelectedAgent(); + int sidx= getSelectedAgentIndex(); if(sidx>=0){ reproduce(sidx, sidx); } @@ -2124,16 +2589,16 @@ void World::selectedBabys() { void World::selectedMutate() { //mutate selected agent - int sidx= getSelectedAgent(); + int sidx= getSelectedAgentIndex(); if(sidx>=0){ agents[sidx].liveMutate(); - if(agents[sidx].id==SELECTION) addEvent("The Selected Agent Was Mutated!", EventColor::PURPLE); + addTipEvent("Selected Agent was Mutated!", EventColor::PURPLE, agents[sidx].id); } } void World::selectedStomachMut() { //rotate selected agent's stomach - int sidx= getSelectedAgent(); + int sidx= getSelectedAgentIndex(); if(sidx>=0){ float temp= agents[sidx].stomach[0]; for(int i=0; i=0){ agents[sidx].printSelf(); } @@ -2153,8 +2618,8 @@ void World::selectedPrint() { void World::selectedTrace(int mode) { //perform a traceback on the selected agent's outputs as called by mode - int sidx= getSelectedAgent(); - if(sidx>=0){ + int sidx= getSelectedAgentIndex(); +/* if(sidx>=0){ if(mode==1){ //get all the wheel output inputs printf("==========TRACEBACK START===========\n"); @@ -2164,12 +2629,12 @@ void World::selectedTrace(int mode) { agents[sidx].traceBack(Output::RIGHT_WHEEL_B); printf("====================================\n"); } - } + } CPBrain NOT IMPLEMENTED*/ } void World::selectedInput(bool state) { //set the selected agent's user input - int sidx= getSelectedAgent(); + int sidx= getSelectedAgentIndex(); if(sidx>=0){ agents[sidx].in[Input::PLAYER]= (float)state; } @@ -2178,7 +2643,7 @@ void World::selectedInput(bool state) { void World::getFollowLocation(float &xi, float &yi) //Control.cpp { - int sidx= getSelectedAgent(); + int sidx= getSelectedAgentIndex(); if (sidx>=0){ xi= agents[sidx].pos.x; yi= agents[sidx].pos.y; @@ -2211,6 +2676,7 @@ bool World::processMouse(int button, int state, int x, int y, float scale) void World::addAgent(Agent &agent) +//Control.cpp??? { agent.id= idcounter; idcounter++; @@ -2219,7 +2685,7 @@ void World::addAgent(Agent &agent) bool World::addLoadedAgent(float x, float y) { - if(loadedagent.brain.boxes.size()==BRAINSIZE){ + if(loadedagent.brain.boxes.size()==BRAINBOXES){ //todo: soon this won't matter Agent a= loadedagent; a.pos.x= x; a.pos.y= y; a.angle= randf(-M_PI,M_PI); @@ -2232,7 +2698,19 @@ void World::addAgents(int num, int set_stomach, bool set_lungs, bool set_temp_pr { for (int i=0;irandseed) randseed=agents[randagent].brain.boxes[i].seed; - for (int j= 0; j0) { - STATalive++; - - if (agents[i].isHerbivore()) { - STATherbivores++; - if (agents[i].gencount>STATbestherbi) STATbestherbi= agents[i].gencount; - } else if (agents[i].isFrugivore()) { - STATfrugivores++; - if (agents[i].gencount>STATbestfrugi) STATbestfrugi= agents[i].gencount; - } else if (agents[i].isCarnivore()) { - STATcarnivores++; - if (agents[i].gencount>STATbestcarni) STATbestcarni= agents[i].gencount; - } - - if (agents[i].isTerrestrial()) { - STATterrans++; - if (agents[i].gencount>STATbestterran) STATbestterran= agents[i].gencount; - } else if (agents[i].isAquatic()) { - STATaquatic++; - if (agents[i].gencount>STATbestaquatic) STATbestaquatic= agents[i].gencount; - } else if (agents[i].isAmphibious()) { - STATamphibians++; - if (agents[i].gencount>STATbestamphibious) STATbestamphibious= agents[i].gencount; - } - - if (agents[i].isSpikey(SPIKELENGTH)) STATspiky++; - - if (agents[i].hybrid) { - STAThybrids++; - if (agents[i].gencount>STATbesthybrid) STATbesthybrid= agents[i].gencount; - } - } - else STATdead++; - } - - //cell layers - for(int i=0;i=0.25) STATplants++; - if(cells[Layer::FRUITS][i][j]>=0.25) STATfruits++; - if(cells[Layer::MEATS][i][j]>=0.25) STATmeats++; - if(cells[Layer::HAZARDS][i][j]>=0.25) STAThazards++; - STATallplant+= cells[Layer::PLANTS][i][j]; - STATallfruit+= cells[Layer::FRUITS][i][j]; - STATallmeat+= cells[Layer::MEATS][i][j]; - STATallhazard+= cells[Layer::HAZARDS][i][j]; - } - } -} - int World::getHerbivores() const { return STATherbivores; @@ -2672,23 +3065,23 @@ float World::getLandRatio() const //count land cells and report as a ratio float World::getFoodSupp() const { - return STATallplant/FOODWASTE*FOODINTAKE/2/100; //#food * 1/FOODWASTE tick/food * FOODINTAKE/1 health/tick * 1/2 agent/health = #agents + return STATallplant/PLANT_WASTE*PLANT_INTAKE/2/100; //#food * 1/PLANT_WASTE tick/food * PLANT_INTAKE/1 health/tick * 1/2 agent/health = #agents // /100 because... reasons } float World::getFruitSupp() const { - return STATallfruit/FRUITWASTE*FRUITINTAKE/2/100; + return STATallfruit/FRUIT_WASTE*FRUIT_INTAKE/2/100; } float World::getMeatSupp() const { - return STATallmeat/MEATWASTE*MEATINTAKE/2/100; + return STATallmeat/MEAT_WASTE*MEAT_INTAKE/2/100; } float World::getHazardSupp() const { - return STATallhazard*HAZARDDAMAGE/2*50; //BUG? #hazard * HAZARDDAMAGE/1/1 health/hazard/tick * 1/2 agent/health != #agents killed per tick + return STATallhazard*HAZARD_DAMAGE/2*50; //BUG? #hazard * HAZARD_DAMAGE/1/1 health/hazard/tick * 1/2 agent/health != #agents killed per tick } /* @@ -2747,6 +3140,20 @@ void World::addEvent(std::string text, int type) events.push_back(data); } +void World::addTipEvent(std::string text, int type, int agentid) +//Control.cpp +{ + //use EventColor:: namespace to access available colors for the "type" input + //post an event only if we: A: tips are enabled, and B: have the current agent selected + if (isAgentSelected(agentid) && !NO_TIPS) { + std::pair > data; + data.first= text; + data.second.first= type; + data.second.second= conf::EVENTS_HALFLIFE; + events.push_back(data); + } +} + void World::dismissNextEvents(int count) //Control.cpp { @@ -2760,6 +3167,13 @@ void World::dismissNextEvents(int count) } } +void World::setStatsAfterLoad() +{ + //reset some stats that often trigger + findStats(); + triggerStatEvents(false); +} + void World::init() { printf("...starting up...\n"); @@ -2769,6 +3183,9 @@ void World::init() NO_TIPS= false; SPAWN_LAKES= conf::SPAWN_LAKES; DISABLE_LAND_SPAWN= conf::DISABLE_LAND_SPAWN; + AMBIENT_LIGHT_PERCENT = conf::AMBIENT_LIGHT_PERCENT; + AGENTS_SEE_CELLS = conf::AGENTS_SEE_CELLS; + AGENTS_DONT_OVERDONATE = conf::AGENTS_DONT_OVERDONATE; MOONLIT= conf::MOONLIT; MOONLIGHTMULT= conf::MOONLIGHTMULT; DROUGHTS= conf::DROUGHTS; @@ -2779,7 +3196,7 @@ void World::init() CLIMATE= conf::CLIMATE; CLIMATE_INTENSITY= conf::CLIMATE_INTENSITY; CLIMATEMULT_AVERAGE= conf::CLIMATEMULT_AVERAGE; - CLIMATE_KILL_FLORA= true; + CLIMATE_AFFECT_FLORA= conf::CLIMATE_AFFECT_FLORA; MIN_PLANT= conf::MIN_PLANT; INITPLANTDENSITY= conf::INITPLANTDENSITY; @@ -2797,24 +3214,31 @@ void World::init() CONTINENTS= conf::CONTINENTS; OCEANPERCENT= conf::OCEANPERCENT; - GRAVITYACCEL= conf::GRAVITYACCEL; + GRAVITY_ACCELERATION= conf::GRAVITY_ACCELERATION; BUMP_PRESSURE= conf::BUMP_PRESSURE; GRAB_PRESSURE= conf::GRAB_PRESSURE; - BRAINSIZE= conf::BRAINSIZE; + BRAINBOXES= conf::BRAINBOXES; + BRAINCONNS= conf::BRAINCONNS; WHEEL_SPEED= conf::WHEEL_SPEED; - BOOSTSIZEMULT= conf::BOOSTSIZEMULT; - BOOSTEXAUSTMULT= conf::BOOSTEXAUSTMULT; + JUMP_MOVE_BONUS_MULT= conf::JUMP_MOVE_BONUS_MULT; + BOOST_MOVE_MULT= conf::BOOST_MOVE_MULT; + BOOST_EXAUSTION_MULT= conf::BOOST_EXAUSTION_MULT; CORPSE_FRAMES= conf::CORPSE_FRAMES; CORPSE_MEAT_MIN= conf::CORPSE_MEAT_MIN; - SOUNDPITCHRANGE= conf::SOUNDPITCHRANGE; + SOUND_PITCH_RANGE= conf::SOUND_PITCH_RANGE; + INV_SOUND_PITCH_RANGE = 1/SOUND_PITCH_RANGE; - FOODTRANSFER= conf::FOODTRANSFER; + GENEROCITY_RATE= conf::GENEROCITY_RATE; BASEEXHAUSTION= conf::BASEEXHAUSTION; - EXHAUSTION_MULT= conf::EXHAUSTION_MULT; + EXHAUSTION_MULT_PER_CONN= conf::EXHAUSTION_MULT_PER_CONN; + EXHAUSTION_MULT_PER_OUTPUT= conf::EXHAUSTION_MULT_PER_OUTPUT; MEANRADIUS= conf::MEANRADIUS; SPIKESPEED= conf::SPIKESPEED; + SPAWN_MIRROR_EYES= conf::SPAWN_MIRROR_EYES; + PRESERVE_MIRROR_EYES= conf::PRESERVE_MIRROR_EYES; FRESHKILLTIME= conf::FRESHKILLTIME; TENDERAGE= conf::TENDERAGE; + INV_TENDERAGE= 1/(float)conf::TENDERAGE; MINMOMHEALTH= conf::MINMOMHEALTH; MIN_INTAKE_HEALTH_RATIO= conf::MIN_INTAKE_HEALTH_RATIO; FUN= 0; @@ -2824,16 +3248,20 @@ void World::init() REP_PER_BABY= conf::REP_PER_BABY; OVERHEAL_REPFILL= conf::OVERHEAL_REPFILL; // LEARNRATE= conf::LEARNRATE; - MAXDEVIATION= conf::MAXDEVIATION; - DEFAULT_MUTCHANCE= conf::DEFAULT_MUTCHANCE; - DEFAULT_MUTSIZE= conf::DEFAULT_MUTSIZE; + OVERRIDE_KINRANGE= conf::OVERRIDE_KINRANGE; + DEFAULT_BRAIN_MUTCHANCE= conf::DEFAULT_BRAIN_MUTCHANCE; + DEFAULT_BRAIN_MUTSIZE= conf::DEFAULT_BRAIN_MUTSIZE; + DEFAULT_GENE_MUTCHANCE= conf::DEFAULT_GENE_MUTCHANCE; + DEFAULT_GENE_MUTSIZE= conf::DEFAULT_GENE_MUTSIZE; LIVE_MUTATE_CHANCE= conf::LIVE_MUTATE_CHANCE; MAXAGE= conf::MAXAGE; MAXWASTEFREQ= conf::MAXWASTEFREQ; - DIST= conf::DIST; - SPIKELENGTH= conf::SPIKELENGTH; - TOOCLOSE= conf::TOOCLOSE; + MAX_SENSORY_DISTANCE= conf::MAX_SENSORY_DISTANCE; + INV_MAX_SENSORY_DISTANCE= 1/MAX_SENSORY_DISTANCE; + SPIKE_LENGTH= conf::SPIKE_LENGTH; + BITE_DISTANCE = conf::BITE_DISTANCE; + BUMP_DAMAGE_OVERLAP= conf::BUMP_DAMAGE_OVERLAP; FOOD_SHARING_DISTANCE= conf::FOOD_SHARING_DISTANCE; SEXTING_DISTANCE= conf::SEXTING_DISTANCE; GRABBING_DISTANCE= conf::GRABBING_DISTANCE; @@ -2842,6 +3270,7 @@ void World::init() HEALTHLOSS_BOOSTMULT= conf::HEALTHLOSS_BOOSTMULT; HEALTHLOSS_BADTEMP= conf::HEALTHLOSS_BADTEMP; HEALTHLOSS_AGING= conf::HEALTHLOSS_AGING; + HEALTHLOSS_EXHAUSTION= conf::HEALTHLOSS_EXHAUSTION; HEALTHLOSS_BRAINUSE= conf::HEALTHLOSS_BRAINUSE; HEALTHLOSS_SPIKE_EXT= conf::HEALTHLOSS_SPIKE_EXT; HEALTHLOSS_BADTERRAIN= conf::HEALTHLOSS_BADTERRAIN; @@ -2852,33 +3281,35 @@ void World::init() DAMAGE_COLLIDE= conf::DAMAGE_COLLIDE; DAMAGE_JAWSNAP= conf::DAMAGE_JAWSNAP; - STOMACH_EFF= conf::STOMACH_EFF; - - FOODINTAKE= conf::FOODINTAKE; - FOODDECAY= conf::FOODDECAY; - FOODGROWTH= conf::FOODGROWTH; - FOODWASTE= conf::FOODWASTE; - FOODADDFREQ= conf::FOODADDFREQ; - FOODSPREAD= conf::FOODSPREAD; - FOODRANGE= conf::FOODRANGE; - - FRUITINTAKE= conf::FRUITINTAKE; - FRUITDECAY= conf::FRUITDECAY; - FRUITWASTE= conf::FRUITWASTE; - FRUITADDFREQ= conf::FRUITADDFREQ; - FRUITREQUIRE= conf::FRUITREQUIRE; - - MEATINTAKE= conf::MEATINTAKE; - MEATDECAY= conf::MEATDECAY; - MEATWASTE= conf::MEATWASTE; - MEATVALUE= conf::MEATVALUE; - - HAZARDFREQ= conf::HAZARDFREQ; - HAZARDEVENT_MULT= conf::HAZARDEVENT_MULT; - HAZARDDECAY= conf::HAZARDDECAY; - HAZARDDEPOSIT= conf::HAZARDDEPOSIT; - HAZARDDAMAGE= conf::HAZARDDAMAGE; - HAZARDPOWER= conf::HAZARDPOWER; + STOMACH_EFFICIENCY= conf::STOMACH_EFFICIENCY; + + PLANT_INTAKE= conf::PLANT_INTAKE; + PLANT_DECAY= conf::PLANT_DECAY; + PLANT_GROWTH= conf::PLANT_GROWTH; + PLANT_WASTE= conf::PLANT_WASTE; + PLANT_ADD_FREQ= conf::PLANT_ADD_FREQ; + PLANT_SPREAD= conf::PLANT_SPREAD; + PLANT_RANGE= conf::PLANT_RANGE; + PLANT_TENACITY= conf::PLANT_TENACITY; + + FRUIT_INTAKE= conf::FRUIT_INTAKE; + FRUIT_DECAY= conf::FRUIT_DECAY; + FRUIT_WASTE= conf::FRUIT_WASTE; + FRUIT_ADD_FREQ= conf::FRUIT_ADD_FREQ; + FRUIT_PLANT_REQUIRE= conf::FRUIT_PLANT_REQUIRE; + + MEAT_INTAKE= conf::MEAT_INTAKE; + MEAT_DECAY= conf::MEAT_DECAY; + MEAT_WASTE= conf::MEAT_WASTE; + MEAT_VALUE= conf::MEAT_VALUE; + MEAT_NON_FRESHKILL_MULT = conf::MEAT_NON_FRESHKILL_MULT; + + HAZARD_EVENT_FREQ= conf::HAZARD_EVENT_FREQ; + HAZARD_EVENT_MULT= conf::HAZARD_EVENT_MULT; + HAZARD_DECAY= conf::HAZARD_DECAY; + HAZARD_DEPOSIT= conf::HAZARD_DEPOSIT; + HAZARD_DAMAGE= conf::HAZARD_DAMAGE; + HAZARD_POWER= conf::HAZARD_POWER; //tip popups. typically the first one is guarenteed to display the moment the sim starts, then each one after has a decreasing //chance of being seen second, third, etc. The first 16 or so are very likely to be seen at least once durring epoch 0 @@ -2893,23 +3324,27 @@ void World::init() tips.push_back("Press 'f' to follow selected agent"); tips.push_back("Zoom with middle mouse click&drag"); tips.push_back("Pan around with left click&drag"); - tips.push_back("Dead agents make 8bit death sound"); tips.push_back("Press 'q' to recenter map & graphs"); tips.push_back("Press 'spacebar' to pause"); - tips.push_back("Press 'h' for detailed interface help"); + tips.push_back("Dead agents make 8bit death sound"); + tips.push_back("Early agents die from exhaustion a lot"); + tips.push_back("Press '1' to select oldest agent"); + tips.push_back("Oldest agent has good chances"); tips.push_back("Demo prevents report.txt changes"); - tips.push_back("Patience, Epoch 0 species are rare"); - tips.push_back("Agent 'whiskers' are eye orientations"); - tips.push_back("These tips may repeat now"); - tips.push_back("Autosave happens every 10 sim-days"); + tips.push_back("Autosaves happen 10 sim days"); tips.push_back("Cycle layer views with 'k' & 'l'"); - tips.push_back("Also zoom with '>' & '<'"); tips.push_back("View all layers again with 'o'"); + tips.push_back("Green bar next to agents is Health"); + tips.push_back("Yellow bar next to agents is Energy"); + tips.push_back("Blue bar is Reproduction Counter"); + tips.push_back("Agent 'whiskers' are eye orientations"); + tips.push_back("Press number keys for auto-selections"); + tips.push_back("These tips may now repeat"); + //20 tips. After this, they may start repeating. I suggest not adding or taking away from above without replacement + tips.push_back("Night and day are simulated"); + tips.push_back("Also zoom with '>' & '<'"); tips.push_back("Press 'h' for detailed interface help"); -// tips.push_back("Press 'g' for graphics details"); tips.push_back("Press 'm' to toggle Fast Mode"); - tips.push_back("Many dead in Epoch 0? It's normal"); - tips.push_back("Select 'New World' to end Demo"); tips.push_back("Agents display splashes for events"); tips.push_back("Yellow spash = agent bit another"); tips.push_back("Orange spash= agent stabbed other"); @@ -2919,19 +3354,74 @@ void World::init() tips.push_back("Purple spash = mutation occured"); tips.push_back("Grey spash = agent was just born"); tips.push_back("Press 'm' to simulate at max speed"); - tips.push_back("Overgrowth Epoch= more plants"); - tips.push_back("Drought Epoch= less plant growth"); - tips.push_back("Mutation x# Epoch= stronger mutations"); + tips.push_back("Select 'New World' to end Demo"); tips.push_back("Use settings.cfg to change sim rules"); + tips.push_back("Hide dead with a UI option or 'j'"); + tips.push_back("Agents can hear each other moving"); + tips.push_back("Agents can hear agents with volume"); + tips.push_back("Agents can see each other's RGB"); + tips.push_back("Agents can smell (count nearby)"); + tips.push_back("Agents can sense blood (1-health)"); + tips.push_back("Agents can boost and double speed"); + tips.push_back("Agents have clock inputs"); + tips.push_back("Agents have a rAnDoM input"); + tips.push_back("Agents have a self-health input"); + tips.push_back("Press 'e' to activate a unique input"); + tips.push_back("Agents seeking grab have cyan limbs"); + tips.push_back("Agents grabbing shows a cyan pulse"); + tips.push_back("Grabbed agents get pushed around"); + tips.push_back("Grab has an angle agents can control"); + tips.push_back("Jumping agents appear closer"); + tips.push_back("Jumping agents can't rotate mid-jump"); + tips.push_back("Jumping agents can't be attacked"); + tips.push_back("Jumping agents can still be seen"); + //start with general world tips. These can be duplicated because it's more random chance once we get more than halfway + tips.push_back("Overgrowth = more plants, fruit"); + tips.push_back("Overgrowth = more plants, fruit"); + tips.push_back("Droughts = less plant growth"); + tips.push_back("Droughts = less plant growth"); + tips.push_back("Moonlight lets agents see at night"); + tips.push_back("Moonlight lets agents see at night"); + tips.push_back("Moonlight lets agents see at night"); + tips.push_back("Hadean Epochs are HOT!"); + tips.push_back("Hadean Epochs are HOT!"); + tips.push_back("Ice Age Epochs are COOL!"); + tips.push_back("Ice Age Epochs are COOL!"); + tips.push_back("Lots of dead on Epoch 0? It's normal"); + tips.push_back("Early agents die a lot"); + tips.push_back("Lots of dead on Epoch 0 is normal"); + tips.push_back("Mutation x# Epochs= more mutations"); + tips.push_back("Mutation x2 = double mutations"); + tips.push_back("Mutation x3 = tripple mutations"); tips.push_back("'report.txt' logs useful data"); - tips.push_back("Epoch 0 is boring. Press 'm'!"); + tips.push_back("Dizzy? Spawned agents like to spin"); + tips.push_back("Dizzy? Spawned agents like to spin"); + tips.push_back("Dizzy? Spawned agents like to spin"); + tips.push_back("Dizzy? Spawned agents like to spin"); + tips.push_back("Dizzy? Spawned agents like to spin"); + tips.push_back("Tiny agents have radius < 5"); + tips.push_back("Tiny agents have radius < 5"); + tips.push_back("Tiny agents have radius < 5"); + tips.push_back("Tiny agents have fewer eyes, ears"); + tips.push_back("Tiny agents have only one clock"); + tips.push_back("Tiny agents can't wield spikes"); + tips.push_back("Tiny agents can't have spikes"); + tips.push_back("Tiny agents can only bite attack"); tips.push_back("The settings.cfg is FUN, isnt it?!"); - tips.push_back("Hide dead with a UI option"); - tips.push_back("Reports periodically saved to file"); tips.push_back("Settings.cfg file can change constants"); - tips.push_back("Press 'm' to also disable Demo Mode"); - tips.push_back("See more options with right-click"); tips.push_back("Use settings.cfg to change constants"); + tips.push_back("Use settings.cfg to change constants"); + tips.push_back("Use settings.cfg to change constants"); + tips.push_back("Use settings.cfg to change constants"); + tips.push_back("You can disable sounds in the UI"); + tips.push_back("You can disable sounds in the UI"); + tips.push_back("You can disable sounds in the UI"); + tips.push_back("You can disable music in the UI"); + tips.push_back("You can disable music in the UI"); + tips.push_back("You can disable music in the UI"); + tips.push_back("See more options with right-click"); + tips.push_back("See more options with right-click"); + tips.push_back("See more options with right-click"); tips.push_back("Contribute on Github!"); tips.push_back("Press 'h' for interface help"); tips.push_back("Tips will be disabled; see settings.cfg"); @@ -2945,6 +3435,11 @@ void World::init() void World::readConfig() { + INITPLANT= -1; //set these to a default value before loading config, so after loading, we read if we changed them! magic + INITFRUIT= -1; + INITMEAT= -1; + INITHAZARD= -1; + //get world constants from config file, settings.cfg char line[256], *pos; char var[32]; @@ -2957,31 +3452,30 @@ void World::readConfig() FILE* cf = fopen("settings.cfg", "r"); if(cf){ - addEvent("settings.cfg detected & loaded", EventColor::CYAN); + addEvent("settings.cfg detected & loaded", EventColor::WHITE); printf("...tweaking the following constants: "); while(!feof(cf)){ fgets(line, sizeof(line), cf); pos= strtok(line,"\n"); sscanf(line, "%s%s", var, dataval); - if(DEBUG) printf("%s\n", var); if(strcmp(var, "V=")==0){ //strcmp= 0 when the arguements equal sscanf(dataval, "%f", &f); if(f!=conf::VERSION) { fclose(cf); printf("CONFIG FILE VERSION MISMATCH!\n EXPECTED 'V= %3.2f', found 'V= %3.2f'\n", conf::VERSION, f); - printf("Need to overwrite config to avoid serious errors!"); + printf("Need to overwrite config to avoid potentially serious errors!\n"); printf("Exit now to keep your custom settings file; otherwise, hit enter to overwrite. . ."); cin.get(); writeConfig(); readConfig(); break; } -// }else if(strcmp(var, "SPAWN_LAKES=")==0){ -// sscanf(dataval, "%i", &i); -// if(i!=(int)SPAWN_LAKES) printf("SPAWN_LAKES, "); -// if(i==1) SPAWN_LAKES= true; -// else SPAWN_LAKES= false; + }else if(strcmp(var, "SPAWN_LAKES=")==0){ + sscanf(dataval, "%i", &i); + if(i!=(int)SPAWN_LAKES) printf("SPAWN_LAKES, "); + if(i==1) SPAWN_LAKES= true; + else SPAWN_LAKES= false; }else if(strcmp(var, "DISABLE_LAND_SPAWN=")==0){ sscanf(dataval, "%i", &i); if(i!=(int)DISABLE_LAND_SPAWN) printf("DISABLE_LAND_SPAWN, "); @@ -2997,6 +3491,20 @@ void World::readConfig() if(i!=(1-(int)DEMO)) printf("DEMO (NO_DEMO), "); if(i==1) DEMO= false; //DEMO vs NO_DEMO: setting is reversed, and we IGNORE it if it's 0 (otherwise we would get weird unintended behavior when loading/saving) //else DEMO= true; + }else if(strcmp(var, "AMBIENT_LIGHT_PERCENT=")==0){ + sscanf(dataval, "%f", &f); + if(f!=AMBIENT_LIGHT_PERCENT) printf("AMBIENT_LIGHT_PERCENT, "); + AMBIENT_LIGHT_PERCENT= f; + }else if(strcmp(var, "AGENTS_SEE_CELLS=")==0){ + sscanf(dataval, "%i", &i); + if(i!=(int)AGENTS_SEE_CELLS) printf("AGENTS_SEE_CELLS, "); + if(i==1) AGENTS_SEE_CELLS= true; + else AGENTS_SEE_CELLS= false; + }else if(strcmp(var, "AGENTS_DONT_OVERDONATE=")==0){ + sscanf(dataval, "%i", &i); + if(i!=(int)AGENTS_DONT_OVERDONATE) printf("AGENTS_DONT_OVERDONATE, "); + if(i==1) AGENTS_DONT_OVERDONATE= true; + else AGENTS_DONT_OVERDONATE= false; }else if(strcmp(var, "MOONLIT=")==0){ sscanf(dataval, "%i", &i); if(i!=(int)MOONLIT) printf("MOONLIT, "); @@ -3041,11 +3549,11 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=CLIMATEMULT_AVERAGE) printf("CLIMATEMULT_AVERAGE, "); CLIMATEMULT_AVERAGE= f; - }else if(strcmp(var, "CLIMATE_KILL_FLORA=")==0){ + }else if(strcmp(var, "CLIMATE_AFFECT_FLORA=")==0){ sscanf(dataval, "%i", &i); - if(i!=(int)CLIMATE_KILL_FLORA) printf("CLIMATE_KILL_FLORA, "); - if(i==1) CLIMATE_KILL_FLORA= true; - else CLIMATE_KILL_FLORA= false; + if(i!=(int)CLIMATE_AFFECT_FLORA) printf("CLIMATE_AFFECT_FLORA, "); + if(i==1) CLIMATE_AFFECT_FLORA= true; + else CLIMATE_AFFECT_FLORA= false; }else if(strcmp(var, "MIN_FOOD=")==0 || strcmp(var, "MINFOOD=")==0){ sscanf(dataval, "%i", &i); if(i!=MIN_PLANT) printf("MIN_FOOD, "); @@ -3118,10 +3626,10 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=OCEANPERCENT) printf("OCEANPERCENT, "); OCEANPERCENT= f; - }else if(strcmp(var, "GRAVITYACCEL=")==0){ + }else if(strcmp(var, "GRAVITY_ACCELERATION=")==0 || strcmp(var, "GRAVITYACCEL=")==0){ sscanf(dataval, "%f", &f); - if(f!=GRAVITYACCEL) printf("GRAVITYACCEL, "); - GRAVITYACCEL= f; + if(f!=GRAVITY_ACCELERATION) printf("GRAVITY_ACCELERATION, "); + GRAVITY_ACCELERATION= f; }else if(strcmp(var, "BUMP_PRESSURE=")==0 || strcmp(var, "REACTPOWER=")==0){ sscanf(dataval, "%f", &f); if(f!=BUMP_PRESSURE) printf("BUMP_PRESSURE, "); @@ -3130,27 +3638,35 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=GRAB_PRESSURE) printf("GRAB_PRESSURE, "); GRAB_PRESSURE= f; - }else if(strcmp(var, "BRAINSIZE=")==0){ + }else if(strcmp(var, "BRAINBOXES=")==0){ sscanf(dataval, "%i", &i); - if(i!=BRAINSIZE) { + if(i!=BRAINBOXES) { if(modcounter!=0) printf("\nALERT: changing brain size is not allowed when the simulation is in progress!\n"); else { - printf("BRAINSIZE, "); - BRAINSIZE= i; + printf("BRAINBOXES, "); + BRAINBOXES= i; } } + }else if(strcmp(var, "BRAINCONNS=")==0){ + sscanf(dataval, "%i", &i); + if(i!=BRAINCONNS) printf("BRAINCONNS, "); + BRAINCONNS= i; }else if(strcmp(var, "WHEEL_SPEED=")==0 || strcmp(var, "BOTSPEED=")==0){ sscanf(dataval, "%f", &f); if(f!=WHEEL_SPEED) printf("WHEEL_SPEED, "); WHEEL_SPEED= f; - }else if(strcmp(var, "BOOSTSIZEMULT=")==0){ + }else if(strcmp(var, "JUMP_MOVE_BONUS_MULT=")==0){ sscanf(dataval, "%f", &f); - if(f!=BOOSTSIZEMULT) printf("BOOSTSIZEMULT, "); - BOOSTSIZEMULT= f; - }else if(strcmp(var, "BOOSTEXAUSTMULT=")==0){ + if(f!=JUMP_MOVE_BONUS_MULT) printf("JUMP_MOVE_BONUS_MULT, "); + JUMP_MOVE_BONUS_MULT= f; + }else if(strcmp(var, "BOOST_MOVE_MULT=")==0 || strcmp(var, "BOOSTSIZEMULT=")==0){ sscanf(dataval, "%f", &f); - if(f!=BOOSTEXAUSTMULT) printf("BOOSTEXAUSTMULT, "); - BOOSTEXAUSTMULT= f; + if(f!=BOOST_MOVE_MULT) printf("BOOST_MOVE_MULT, "); + BOOST_MOVE_MULT= f; + }else if(strcmp(var, "BOOST_EXAUSTION_MULT=")==0 || strcmp(var, "BOOSTEXAUSTMULT=")==0){ + sscanf(dataval, "%f", &f); + if(f!=BOOST_EXAUSTION_MULT) printf("BOOST_EXAUSTION_MULT, "); + BOOST_EXAUSTION_MULT= f; }else if(strcmp(var, "CORPSE_FRAMES=")==0){ sscanf(dataval, "%i", &i); if(i!=CORPSE_FRAMES) printf("CORPSE_FRAMES, "); @@ -3159,22 +3675,26 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=CORPSE_MEAT_MIN) printf("CORPSE_MEAT_MIN, "); CORPSE_MEAT_MIN= f; - }else if(strcmp(var, "SOUNDPITCHRANGE=")==0){ + }else if(strcmp(var, "SOUND_PITCH_RANGE=")==0 || strcmp(var, "SOUNDPITCHRANGE=")==0){ sscanf(dataval, "%f", &f); - if(f!=SOUNDPITCHRANGE) printf("SOUNDPITCHRANGE, "); - SOUNDPITCHRANGE= f; - }else if(strcmp(var, "FOODTRANSFER=")==0){ + if(f!=SOUND_PITCH_RANGE) printf("SOUND_PITCH_RANGE, "); + SOUND_PITCH_RANGE= f; + }else if(strcmp(var, "GENEROCITY_RATE=")==0 || strcmp(var, "FOODTRANSFER=")==0){ sscanf(dataval, "%f", &f); - if(f!=FOODTRANSFER) printf("FOODTRANSFER, "); - FOODTRANSFER= f; + if(f!=GENEROCITY_RATE) printf("GENEROCITY_RATE, "); + GENEROCITY_RATE= f; }else if(strcmp(var, "BASEEXHAUSTION=")==0){ sscanf(dataval, "%f", &f); if(f!=BASEEXHAUSTION) printf("BASEEXHAUSTION, "); BASEEXHAUSTION= f; - }else if(strcmp(var, "EXHAUSTION_MULT=")==0){ + }else if(strcmp(var, "EXHAUSTION_MULT_PER_OUTPUT=")==0){ + sscanf(dataval, "%f", &f); + if(f!=EXHAUSTION_MULT_PER_OUTPUT) printf("EXHAUSTION_MULT_PER_OUTPUT, "); + EXHAUSTION_MULT_PER_OUTPUT= f; + }else if(strcmp(var, "EXHAUSTION_MULT_PER_CONN=")==0){ sscanf(dataval, "%f", &f); - if(f!=EXHAUSTION_MULT) printf("EXHAUSTION_MULT, "); - EXHAUSTION_MULT= f; + if(f!=EXHAUSTION_MULT_PER_CONN) printf("EXHAUSTION_MULT_PER_CONN, "); + EXHAUSTION_MULT_PER_CONN= f; }else if(strcmp(var, "MEANRADIUS=")==0){ sscanf(dataval, "%f", &f); if(f!=MEANRADIUS) printf("MEANRADIUS, "); @@ -3183,6 +3703,16 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=SPIKESPEED) printf("SPIKESPEED, "); SPIKESPEED= f; + }else if(strcmp(var, "SPAWN_MIRROR_EYES=")==0){ + sscanf(dataval, "%i", &i); + if(i!=(int)SPAWN_MIRROR_EYES) printf("SPAWN_MIRROR_EYES, "); + if(i==1) SPAWN_MIRROR_EYES= true; + else SPAWN_MIRROR_EYES= false; + }else if(strcmp(var, "PRESERVE_MIRROR_EYES=")==0){ + sscanf(dataval, "%i", &i); + if(i!=(int)PRESERVE_MIRROR_EYES) printf("PRESERVE_MIRROR_EYES, "); + if(i==1) PRESERVE_MIRROR_EYES= true; + else PRESERVE_MIRROR_EYES= false; }else if(strcmp(var, "FRESHKILLTIME=")==0){ sscanf(dataval, "%i", &i); if(i!=FRESHKILLTIME) printf("FRESHKILLTIME, "); @@ -3211,7 +3741,9 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=SUN_GRE) printf("SUN_GRE, "); SUN_GRE= f; - }else if(strcmp(var, "SUN_BLU=")==0){ + } + //hit a compiler limit; resetting the if blocks + if(strcmp(var, "SUN_BLU=")==0){ sscanf(dataval, "%f", &f); if(f!=SUN_BLU) printf("SUN_BLU, "); SUN_BLU= f; @@ -3227,18 +3759,42 @@ void World::readConfig() // sscanf(dataval, "%f", &f); // if(f!=LEARNRATE) printf("LEARNRATE, "); // LEARNRATE= f; - }else if(strcmp(var, "MAXDEVIATION=")==0){ + }else if(strcmp(var, "OVERRIDE_KINRANGE=")==0){ + sscanf(dataval, "%i", &i); + if(i!=OVERRIDE_KINRANGE) printf("OVERRIDE_KINRANGE, "); + OVERRIDE_KINRANGE= i; + }else if(strcmp(var, "MAXDEVIATION=")==0){ //MAXDEVIATION is an old config value for OVERRIDE_KINRANGE that was saved in float form sscanf(dataval, "%f", &f); - if(f!=MAXDEVIATION) printf("MAXDEVIATION, "); - MAXDEVIATION= f; - }else if(strcmp(var, "DEFAULT_MUTCHANCE=")==0 || strcmp(var, "MUTCHANCE=")==0){ + if((int)f!=OVERRIDE_KINRANGE) printf("OVERRIDE_KINRANGE (MAXDEVIATION), "); + OVERRIDE_KINRANGE= (int)f; + }else if(strcmp(var, "DEFAULT_BRAIN_MUTCHANCE=")==0){ sscanf(dataval, "%f", &f); - if(f!=DEFAULT_MUTCHANCE) printf("DEFAULT_MUTCHANCE, "); - DEFAULT_MUTCHANCE= f; - }else if(strcmp(var, "DEFAULT_MUTSIZE=")==0 || strcmp(var, "MUTSIZE=")==0){ + if(f!=DEFAULT_BRAIN_MUTCHANCE) printf("DEFAULT_BRAIN_MUTCHANCE, "); + DEFAULT_BRAIN_MUTCHANCE= f; + }else if(strcmp(var, "DEFAULT_BRAIN_MUTSIZE=")==0){ sscanf(dataval, "%f", &f); - if(f!=DEFAULT_MUTSIZE) printf("DEFAULT_MUTSIZE, "); - DEFAULT_MUTSIZE= f; + if(f!=DEFAULT_BRAIN_MUTSIZE) printf("DEFAULT_BRAIN_MUTSIZE, "); + DEFAULT_BRAIN_MUTSIZE= f; + }else if(strcmp(var, "DEFAULT_GENE_MUTCHANCE=")==0){ + sscanf(dataval, "%f", &f); + if(f!=DEFAULT_GENE_MUTCHANCE) printf("DEFAULT_GENE_MUTCHANCE, "); + DEFAULT_GENE_MUTCHANCE= f; + }else if(strcmp(var, "DEFAULT_GENE_MUTSIZE=")==0){ + sscanf(dataval, "%f", &f); + if(f!=DEFAULT_GENE_MUTSIZE) printf("DEFAULT_GENE_MUTSIZE, "); + DEFAULT_GENE_MUTSIZE= f; + }else if(strcmp(var, "MUTCHANCE=")==0){ + sscanf(dataval, "%f", &f); + if(f!=DEFAULT_BRAIN_MUTCHANCE) printf("DEFAULT_BRAIN_MUTCHANCE, "); + if(f!=DEFAULT_GENE_MUTCHANCE) printf("DEFAULT_GENE_MUTCHANCE, "); + DEFAULT_BRAIN_MUTCHANCE= f; + DEFAULT_GENE_MUTCHANCE= f; + }else if(strcmp(var, "MUTSIZE=")==0){ + sscanf(dataval, "%f", &f); + if(f!=DEFAULT_BRAIN_MUTSIZE) printf("DEFAULT_BRAIN_MUTSIZE, "); + if(f!=DEFAULT_GENE_MUTSIZE) printf("DEFAULT_GENE_MUTSIZE, "); + DEFAULT_BRAIN_MUTSIZE= f; + DEFAULT_GENE_MUTSIZE= f; }else if(strcmp(var, "LIVE_MUTATE_CHANCE=")==0){ sscanf(dataval, "%f", &f); if(f!=LIVE_MUTATE_CHANCE) printf("LIVE_MUTATE_CHANCE, "); @@ -3251,18 +3807,22 @@ void World::readConfig() sscanf(dataval, "%i", &i); if(i!=MAXWASTEFREQ) printf("MAXWASTEFREQ, "); MAXWASTEFREQ= i; - }else if(strcmp(var, "DIST=")==0){ + }else if(strcmp(var, "MAX_SENSORY_DISTANCE=")==0 || strcmp(var, "DIST=")==0){ sscanf(dataval, "%f", &f); - if(f!=DIST) printf("DIST, "); - DIST= f; - }else if(strcmp(var, "SPIKELENGTH=")==0){ + if(f!=MAX_SENSORY_DISTANCE) printf("MAX_SENSORY_DISTANCE, "); + MAX_SENSORY_DISTANCE= f; + }else if(strcmp(var, "SPIKE_LENGTH=")==0 || strcmp(var, "SPIKELENGTH=")==0){ sscanf(dataval, "%f", &f); - if(f!=SPIKELENGTH) printf("SPIKELENGTH, "); - SPIKELENGTH= f; - }else if(strcmp(var, "TOOCLOSE=")==0){ + if(f!=SPIKE_LENGTH) printf("SPIKE_LENGTH, "); + SPIKE_LENGTH= f; + }else if(strcmp(var, "BITE_DISTANCE=")==0){ sscanf(dataval, "%f", &f); - if(f!=TOOCLOSE) printf("TOOCLOSE, "); - TOOCLOSE= f; + if(f!=BITE_DISTANCE) printf("BITE_DISTANCE, "); + BITE_DISTANCE= f; + }else if(strcmp(var, "BUMP_DAMAGE_OVERLAP=")==0 || strcmp(var, "TOOCLOSE=")==0){ + sscanf(dataval, "%f", &f); + if(f!=BUMP_DAMAGE_OVERLAP) printf("BUMP_DAMAGE_OVERLAP, "); + BUMP_DAMAGE_OVERLAP= f; }else if(strcmp(var, "FOOD_SHARING_DISTANCE=")==0){ sscanf(dataval, "%f", &f); if(f!=FOOD_SHARING_DISTANCE) printf("FOOD_SHARING_DISTANCE, "); @@ -3291,6 +3851,10 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=HEALTHLOSS_AGING) printf("HEALTHLOSS_AGING, "); HEALTHLOSS_AGING= f; + }else if(strcmp(var, "HEALTHLOSS_EXHAUSTION=")==0){ + sscanf(dataval, "%f", &f); + if(f!=HEALTHLOSS_EXHAUSTION) printf("HEALTHLOSS_EXHAUSTION, "); + HEALTHLOSS_EXHAUSTION= f; }else if(strcmp(var, "HEALTHLOSS_BRAINUSE=")==0){ sscanf(dataval, "%f", &f); if(f!=HEALTHLOSS_BRAINUSE) printf("HEALTHLOSS_BRAINUSE, "); @@ -3323,98 +3887,106 @@ void World::readConfig() sscanf(dataval, "%f", &f); if(f!=DAMAGE_JAWSNAP) printf("DAMAGE_JAWSNAP, "); DAMAGE_JAWSNAP= f; - }else if(strcmp(var, "STOMACH_EFF=")==0){ + }else if(strcmp(var, "STOMACH_EFFICIENCY=")==0 || strcmp(var, "STOMACH_EFF=")==0){ sscanf(dataval, "%f", &f); - if(f!=STOMACH_EFF) printf("STOMACH_EFF, "); - STOMACH_EFF= f; - }else if(strcmp(var, "FOODINTAKE=")==0){ + if(f!=STOMACH_EFFICIENCY) printf("STOMACH_EFFICIENCY, "); + STOMACH_EFFICIENCY= f; + }else if(strcmp(var, "PLANT_INTAKE=")==0){ sscanf(dataval, "%f", &f); - if(f!=FOODINTAKE) printf("FOODINTAKE, "); - FOODINTAKE= f; - }else if(strcmp(var, "FOODDECAY=")==0){ + if(f!=PLANT_INTAKE) printf("PLANT_INTAKE, "); + PLANT_INTAKE= f; + }else if(strcmp(var, "PLANT_DECAY=")==0){ sscanf(dataval, "%f", &f); - if(f!=FOODDECAY) printf("FOODDECAY, "); - FOODDECAY= f; - }else if(strcmp(var, "FOODGROWTH=")==0){ + if(f!=PLANT_DECAY) printf("PLANT_DECAY, "); + PLANT_DECAY= f; + }else if(strcmp(var, "PLANT_GROWTH=")==0){ sscanf(dataval, "%f", &f); - if(f!=FOODGROWTH) printf("FOODGROWTH, "); - FOODGROWTH= f; - }else if(strcmp(var, "FOODWASTE=")==0){ + if(f!=PLANT_GROWTH) printf("PLANT_GROWTH, "); + PLANT_GROWTH= f; + }else if(strcmp(var, "PLANT_WASTE=")==0){ sscanf(dataval, "%f", &f); - if(f!=FOODWASTE) printf("FOODWASTE, "); - FOODWASTE= f; - }else if(strcmp(var, "FOODADDFREQ=")==0){ + if(f!=PLANT_WASTE) printf("PLANT_WASTE, "); + PLANT_WASTE= f; + }else if(strcmp(var, "PLANT_ADD_FREQ=")==0){ sscanf(dataval, "%i", &i); - if(i!=FOODADDFREQ) printf("FOODADDFREQ, "); - FOODADDFREQ= i; - }else if(strcmp(var, "FOODSPREAD=")==0){ + if(i!=PLANT_ADD_FREQ) printf("PLANT_ADD_FREQ, "); + PLANT_ADD_FREQ= i; + }else if(strcmp(var, "PLANT_SPREAD=")==0){ sscanf(dataval, "%f", &f); - if(f!=FOODSPREAD) printf("FOODSPREAD, "); - FOODSPREAD= f; - }else if(strcmp(var, "FOODRANGE=")==0){ + if(f!=PLANT_SPREAD) printf("PLANT_SPREAD, "); + PLANT_SPREAD= f; + }else if(strcmp(var, "PLANT_RANGE=")==0){ sscanf(dataval, "%i", &i); - if(i!=FOODRANGE) printf("FOODRANGE, "); - FOODRANGE= i; - }else if(strcmp(var, "FRUITINTAKE=")==0){ + if(i!=PLANT_RANGE) printf("PLANT_RANGE, "); + PLANT_RANGE= i; + }else if(strcmp(var, "PLANT_TENACITY=")==0){ sscanf(dataval, "%f", &f); - if(f!=FRUITINTAKE) printf("FRUITINTAKE, "); - FRUITINTAKE= f; - }else if(strcmp(var, "FRUITDECAY=")==0){ + if(f!=PLANT_TENACITY) printf("PLANT_TENACITY, "); + PLANT_TENACITY= f; + }else if(strcmp(var, "FRUIT_INTAKE=")==0){ sscanf(dataval, "%f", &f); - if(f!=FRUITDECAY) printf("FRUITDECAY, "); - FRUITDECAY= f; - }else if(strcmp(var, "FRUITWASTE=")==0){ + if(f!=FRUIT_INTAKE) printf("FRUIT_INTAKE, "); + FRUIT_INTAKE= f; + }else if(strcmp(var, "FRUIT_DECAY=")==0){ sscanf(dataval, "%f", &f); - if(f!=FRUITWASTE) printf("FRUITWASTE, "); - FRUITWASTE= f; - }else if(strcmp(var, "FRUITADDFREQ=")==0){ + if(f!=FRUIT_DECAY) printf("FRUIT_DECAY, "); + FRUIT_DECAY= f; + }else if(strcmp(var, "FRUIT_WASTE=")==0){ + sscanf(dataval, "%f", &f); + if(f!=FRUIT_WASTE) printf("FRUIT_WASTE, "); + FRUIT_WASTE= f; + }else if(strcmp(var, "FRUIT_ADD_FREQ=")==0){ sscanf(dataval, "%i", &i); - if(i!=FRUITADDFREQ) printf("FRUITADDFREQ, "); - FRUITADDFREQ= i; - }else if(strcmp(var, "FRUITREQUIRE=")==0){ + if(i!=FRUIT_ADD_FREQ) printf("FRUIT_ADD_FREQ, "); + FRUIT_ADD_FREQ= i; + }else if(strcmp(var, "FRUIT_PLANT_REQUIRE=")==0){ + sscanf(dataval, "%f", &f); + if(f!=FRUIT_PLANT_REQUIRE) printf("FRUIT_PLANT_REQUIRE, "); + FRUIT_PLANT_REQUIRE= f; + }else if(strcmp(var, "MEAT_INTAKE=")==0){ sscanf(dataval, "%f", &f); - if(f!=FRUITREQUIRE) printf("FRUITREQUIRE, "); - FRUITREQUIRE= f; - }else if(strcmp(var, "MEATINTAKE=")==0){ + if(f!=MEAT_INTAKE) printf("MEAT_INTAKE, "); + MEAT_INTAKE= f; + }else if(strcmp(var, "MEAT_DECAY=")==0){ sscanf(dataval, "%f", &f); - if(f!=MEATINTAKE) printf("MEATINTAKE, "); - MEATINTAKE= f; - }else if(strcmp(var, "MEATDECAY=")==0){ + if(f!=MEAT_DECAY) printf("MEAT_DECAY, "); + MEAT_DECAY= f; + }else if(strcmp(var, "MEAT_WASTE=")==0){ sscanf(dataval, "%f", &f); - if(f!=MEATDECAY) printf("MEATDECAY, "); - MEATDECAY= f; - }else if(strcmp(var, "MEATWASTE=")==0){ + if(f!=MEAT_WASTE) printf("MEAT_WASTE, "); + MEAT_WASTE= f; + }else if(strcmp(var, "MEAT_VALUE=")==0){ sscanf(dataval, "%f", &f); - if(f!=MEATWASTE) printf("MEATWASTE, "); - MEATWASTE= f; - }else if(strcmp(var, "MEATVALUE=")==0){ + if(f!=MEAT_VALUE) printf("MEAT_VALUE, "); + MEAT_VALUE= f; + }else if(strcmp(var, "MEAT_NON_FRESHKILL_MULT=")==0){ sscanf(dataval, "%f", &f); - if(f!=MEATVALUE) printf("MEATVALUE, "); - MEATVALUE= f; - }else if(strcmp(var, "HAZARDFREQ=")==0){ + if(f!=MEAT_NON_FRESHKILL_MULT) printf("MEAT_NON_FRESHKILL_MULT, "); + MEAT_NON_FRESHKILL_MULT= f; + }else if(strcmp(var, "HAZARD_EVENT_FREQ=")==0){ sscanf(dataval, "%i", &i); - if(i!=HAZARDFREQ) printf("HAZARDFREQ, "); - HAZARDFREQ= i; - }else if(strcmp(var, "HAZARDEVENT_MULT=")==0){ + if(i!=HAZARD_EVENT_FREQ) printf("HAZARD_EVENT_FREQ, "); + HAZARD_EVENT_FREQ= i; + }else if(strcmp(var, "HAZARD_EVENT_MULT=")==0){ sscanf(dataval, "%f", &f); - if(f!=HAZARDEVENT_MULT) printf("HAZARDEVENT_MULT, "); - HAZARDEVENT_MULT= f; - }else if(strcmp(var, "HAZARDDECAY=")==0){ + if(f!=HAZARD_EVENT_MULT) printf("HAZARD_EVENT_MULT, "); + HAZARD_EVENT_MULT= f; + }else if(strcmp(var, "HAZARD_DECAY=")==0){ sscanf(dataval, "%f", &f); - if(f!=HAZARDDECAY) printf("HAZARDDECAY, "); - HAZARDDECAY= f; - }else if(strcmp(var, "HAZARDDEPOSIT=")==0){ + if(f!=HAZARD_DECAY) printf("HAZARD_DECAY, "); + HAZARD_DECAY= f; + }else if(strcmp(var, "HAZARD_DEPOSIT=")==0){ sscanf(dataval, "%f", &f); - if(f!=HAZARDDEPOSIT) printf("HAZARDDEPOSIT, "); - HAZARDDEPOSIT= f; - }else if(strcmp(var, "HAZARDDAMAGE=")==0){ + if(f!=HAZARD_DEPOSIT) printf("HAZARD_DEPOSIT, "); + HAZARD_DEPOSIT= f; + }else if(strcmp(var, "HAZARD_DAMAGE=")==0){ sscanf(dataval, "%f", &f); - if(f!=HAZARDDAMAGE) printf("HAZARDDAMAGE, "); - HAZARDDAMAGE= f; - }else if(strcmp(var, "HAZARDPOWER=")==0){ + if(f!=HAZARD_DAMAGE) printf("HAZARD_DAMAGE, "); + HAZARD_DAMAGE= f; + }else if(strcmp(var, "HAZARD_POWER=")==0){ sscanf(dataval, "%f", &f); - if(f!=HAZARDPOWER) printf("HAZARDPOWER, "); - HAZARDPOWER= f; + if(f!=HAZARD_POWER) printf("HAZARD_POWER, "); + HAZARD_POWER= f; } } if(cf){ @@ -3429,6 +4001,38 @@ void World::readConfig() writeConfig(); #endif } + + //set some other variables that are based on the possibly loaded config values + CW= conf::WIDTH/conf::CZ; + CH= conf::HEIGHT/conf::CZ; //note: may add custom variables from loaded saves/config here eventually + int CS= CW*CH; + + if(INITPLANT==-1) INITPLANT = (int)(INITPLANTDENSITY*CS); + if(INITFRUIT==-1) INITFRUIT = (int)(INITFRUITDENSITY*CS); + if(INITMEAT==-1) INITMEAT = (int)(INITMEATDENSITY*CS); + if(INITHAZARD==-1) INITHAZARD = (int)(INITHAZARDDENSITY*CS); + if(INITPLANT>CS) INITPLANT= CS; + if(INITFRUIT>CS) INITFRUIT= CS; + if(INITMEAT>CS) INITMEAT= CS; + if(INITHAZARD>CS) INITHAZARD= CS; + + if(DEBUG){ + printf("INITPLANT: %i\n", INITPLANT); + printf("INITFRUIT: %i\n", INITFRUIT); + printf("INITMEAT: %i\n", INITMEAT); + printf("INITHAZARD: %i\n", INITHAZARD); + } + + if(BRAINBOXES < Output::OUTPUT_SIZE) { + printf("BRAINBOXES config value was too small. It has been reset to min allowed (number of Outputs)\n"); + BRAINBOXES = Output::OUTPUT_SIZE; + } + + //set the highest distance config value as the distance within which we check if an agent is "near" another, for tree processing + NEAREST= max(max(max(max(FOOD_SHARING_DISTANCE, SEXTING_DISTANCE), GRABBING_DISTANCE), SPIKE_LENGTH + MEANRADIUS*3), BITE_DISTANCE + MEANRADIUS*3); + INV_MAX_SENSORY_DISTANCE = 1/MAX_SENSORY_DISTANCE; + INV_TENDERAGE = 1/(float)TENDERAGE; + INV_SOUND_PITCH_RANGE = 1/SOUND_PITCH_RANGE; } void World::writeConfig() @@ -3448,31 +4052,34 @@ void World::writeConfig() fprintf(cf, "V= %f \t\t\t//Version number this file was created in. If different than program's, will ask user to exit, or it will overwrite.\n", conf::VERSION); fprintf(cf, "\n"); fprintf(cf, "NO_TIPS= %i \t\t\t//if true (=1), prevents tips from being displayed. Default= 1 when writing a new config\n", 1); - fprintf(cf, "NO_DEMO= %i \t\t\t//if true (=1), this will prevent demo mode from running at start. Default= 1 when writing a new config\n", 1); -// fprintf(cf, "SPAWN_LAKES= %i \t\t\t//if true (=1), and if terrain generation forms too much or too little land, the generator takes a moment to put in lakes (or islands)\n", NO_TIPS); + fprintf(cf, "NO_DEMO= %i \t\t\t//if true (=1), this will start Evagents normally. Demo Mode prevents the report.txt from being overwritten during Epoch 0, and always disables itself on Epoch 1. Meant to prevent deletion of previous report.txts if you forgot to make a copy. Set to 0 to allow Demo Mode to start when you relaunch the program.\n", 1); +// fprintf(cf, "SPAWN_LAKES= %i \t\t\t//if true (=1), and if terrain generation forms too much or too little land, the generator takes a moment to put in lakes (or islands)\n", conf::SPAWN_LAKES); fprintf(cf, "DISABLE_LAND_SPAWN= %i \t\t//true-false flag for disabling agents from spawning on land. 0= land spawn allowed, 1= not allowed. Is GUI-controllable. This value is whatever was set in program when this file was saved\n", DISABLE_LAND_SPAWN); + fprintf(cf, "AMBIENT_LIGHT_PERCENT= %f //multiplier of the natural light level that eyes will see. Be cautious with this; too high (>0.5) and cells will wash out agents. Note, if AGENTS_SEE_CELLS is enabled, then this gets applied to the cell colors instead\n", conf::AMBIENT_LIGHT_PERCENT); + fprintf(cf, "AGENTS_SEE_CELLS= %i \t\t//true-false flag for letting agents see cells. 0= agents only see other agents and an ambient day/night brightness, 1= agents see agents and cells with day/night brightness applied, and is considerably laggier. Is saved/loaded. Default is 1.\n", conf::AGENTS_SEE_CELLS); + fprintf(cf, "AGENTS_DONT_OVERDONATE= %i \t//true-false flag for letting agents over-donate heath to others. 0= agents will give freely as long as they want to, even to death, 1= agents will only give up to half the difference in health, meaning everyone gets equal amounts of health. Default is 0.\n", conf::AGENTS_DONT_OVERDONATE); fprintf(cf, "MOONLIT= %i \t\t\t//true-false flag for letting agents see other agents at night. 0= no eyesight, 1= see agents at half light. Is GUI-controllable and saved/loaded. This value is whatever was set in program when this file was saved\n", MOONLIT); - fprintf(cf, "MOONLIGHTMULT= %f \t\t//the amount of minimum light MOONLIT provides. If set to 1, daylight cycles are not processed at all and the world becomes bathed in eternal sunlight. 0.1 default\n", conf::MOONLIGHTMULT); + fprintf(cf, "MOONLIGHTMULT= %f \t//the amount of minimum light MOONLIT provides. If set to 1, daylight cycles are not processed at all and the world becomes bathed in eternal sunlight. 0.1 default\n", conf::MOONLIGHTMULT); fprintf(cf, "DROUGHTS= %i \t\t\t//true-false flag for if the drought/overgrowth mechanic is enabled. 0= constant normal growth, 1= variable. Is GUI-controllable and saved/loaded. This value is whatever was set in program when this file was saved\n", DROUGHTS); fprintf(cf, "DROUGHT_MIN= %f \t\t//minimum multiplier value of droughts, below which it gets pushed back toward 1.0\n", conf::DROUGHT_MIN); fprintf(cf, "DROUGHT_MAX= %f \t\t//maximum multiplier value of droughts (overgrowth), above which it gets pushed back toward 1.0\n", conf::DROUGHT_MAX); fprintf(cf, "MUTEVENTS= %i \t\t\t//true-false flag for if the mutation event mechanic is enabled. 0= constant mutation rates, 1= variable. Is GUI-controllable and saved/loaded. This value is whatever was set in program when this file was saved\n", MUTEVENTS); - fprintf(cf, "MUTEVENT_MAX= %i \t\t//integer max possible multiplier for mutation event mechanic. =1 effectively disables. =0 enables 50%% chance of no live mutations epoch. Negative values increase this chance (-1 => 66%%, -2 => 75%%, etc), thereby decreasing the chance of any live mutation epochs at all. Useful if you want to increase base mutation rates.\n", conf::MUTEVENT_MAX); + fprintf(cf, "MUTEVENT_MAX= %i \t\t//integer max possible multiplier for mutation event mechanic. =1 effectively disables. =0 enables 50%% chance of no live mutations epoch. Negative values increase this chance (-1 => 66%%, -2 => 75%%, etc), thereby decreasing the chance of any live mutations at all. Positive values are useful if you want to increase base mutation rates.\n", conf::MUTEVENT_MAX); fprintf(cf, "CLIMATE= %i \t\t\t//true-false flag for if the global climate mechanic is enabled. 0= temperature ranges are locked in at spawn, 1= variable temp over time. Is GUI-controllable and saved/loaded. This value is whatever was set in program when this file was saved\n", CLIMATE); fprintf(cf, "CLIMATE_INTENSITY= %f \t//intensity multiplier of climate changes (effects both bias and mult). If this is too large (>0.01) climate will swing wildly. GUI-controlable, NOT saved with worlds. This value is whatever was set in program when this file was saved.\n", CLIMATE_INTENSITY); fprintf(cf, "CLIMATEMULT_AVERAGE= %f \t//the value that the climate multiplier gets pushed back towards every 0.5 epochs. Reminder: A CLIMATEMULT of 0 means uniform temp everywhere, =1 and no matter what the CLIMATEBIAS is, the full possible range of temperature exists. This value is whatever was set in program when this file was saved. Default= 0.5\n", CLIMATEMULT_AVERAGE); - fprintf(cf, "CLIMATE_KILL_FLORA= %i \t//true-false flag for if the global climate destroys plant life at the extremes. 0= temperature has no affect, 1= plant life dies at extreme temperatures (V0.06- behavior). Default= 1 when writing a new config\n", 1); + fprintf(cf, "CLIMATE_AFFECT_FLORA= %i \t//true-false flag for if the global climate destroys plant life at the extremes. 0= temperature has no affect, 1= plant life dies at extreme temperatures (V0.06- behavior). Default= 1 when writing a new config\n", 1); fprintf(cf, "\n"); fprintf(cf, "MINFOOD= %i \t\t\t//Minimum number of food cells which must have food during simulation. 0= off\n", conf::MIN_PLANT); fprintf(cf, "INITFOODDENSITY= %f \t//initial density of full food cells. Is a decimal percentage of the world cells. Use 'INITFOOD= #' to set a number\n", conf::INITPLANTDENSITY); - fprintf(cf, "//INITFOOD= %i \t\t\t//remove '//' from before the flag to enable, overrides INITFOODDENSITY, is a raw # of cells which must have food at init\n", 0); + fprintf(cf, "//INITFOOD= %i \t\t\t//remove '//' from before the flag to enable, overrides INITFOODDENSITY, this is a raw # of cells which must have food at init\n", 0); fprintf(cf, "INITFRUITDENSITY= %f \t//initial density of full fruit cells. Is a decimal percentage of the world cells.\n", conf::INITFRUITDENSITY); - fprintf(cf, "//INITFRUIT= %i \t\t\t//remove '//' from before the flag to enable, overrides INITFRUITDENSITY, is a raw # of cells which must have fruit at init\n", 0); + fprintf(cf, "//INITFRUIT= %i \t\t\t//remove '//' from before the flag to enable, overrides INITFRUITDENSITY, this is a raw # of cells which must have fruit at init\n", 0); fprintf(cf, "INITMEATDENSITY= %f \t//initial density of full meat cells. Is a decimal percentage of the world cells. Use 'INITMEAT= #' to set a number\n", conf::INITMEATDENSITY); - fprintf(cf, "//INITMEAT= %i \t\t\t//remove '//' from before the flag to enable, overrides INITMEATDENSITY, is a raw # of cells which must have meat at init\n", 0); + fprintf(cf, "//INITMEAT= %i \t\t\t//remove '//' from before the flag to enable, overrides INITMEATDENSITY, this is a raw # of cells which must have meat at init\n", 0); fprintf(cf, "INITHAZARDDENSITY= %f \t//initial density of full hazard cells. Is a decimal percentage of the world cells.\n", conf::INITHAZARDDENSITY); - fprintf(cf, "//INITHAZARD= %i \t\t//remove '//' from before the flag to enable, overrides INITHAZARDDENSITY, is a raw # of cells which must have hazard at init\n", 0); - fprintf(cf, "AGENTS_MIN_NOTCLOSED= %i \t//minimum number of agents. Random agents will get spawned in until this number is reached.\n", conf::AGENTS_MIN_NOTCLOSED); + fprintf(cf, "//INITHAZARD= %i \t\t//remove '//' from before the flag to enable, overrides INITHAZARDDENSITY, this is a raw # of cells which must have hazard at init\n", 0); + fprintf(cf, "AGENTS_MIN_NOTCLOSED= %i \t//minimum number of agents. Random agents will get spawned in until this number is reached while the world is Open (as opposed to Closed, or all random spawns disabled).\n", conf::AGENTS_MIN_NOTCLOSED); fprintf(cf, "AGENTS_MAX_SPAWN= %i \t\t//if more agents than this number exist (live agents), random spawns no longer occur\n", conf::AGENTS_MAX_SPAWN); fprintf(cf, "AGENTSPAWN_FREQ= %i \t\t//how often does a random agent get spawned while below ENOUGHAGENTS? Higher values are less frequent\n", conf::AGENTSPAWN_FREQ); fprintf(cf, "AGENTS_MAX_NOOXYGEN= %i \t//number of agents at which the full HEALTHLOSS_NOOXYGEN is applied\n", conf::AGENTS_MAX_NOOXYGEN); @@ -3483,88 +4090,98 @@ void World::writeConfig() fprintf(cf, "\n"); fprintf(cf, "CONTINENTS= %i \t\t\t//number of 'continents' generated on the land layer. Not guaranteed to be accurate\n", conf::CONTINENTS); fprintf(cf, "OCEANPERCENT= %f \t\t//decimal ratio of terrain layer which will be ocean. Approximately\n", conf::OCEANPERCENT); - fprintf(cf, "GRAVITYACCEL= %f \t\t//how fast an agent will 'fall' after jumping. 0= jump disabled, 0.1+ = super-gravity\n", conf::GRAVITYACCEL); - fprintf(cf, "BUMP_PRESSURE= %f \t//the restoring force between two colliding agents. 0= no reaction (disables TOOCLOSE and all collisions). I'd avoid negative values if I were you...\n", conf::BUMP_PRESSURE); - fprintf(cf, "GRAB_PRESSURE= %f \t//the restoring force between and agent and its grab target. 0= no reaction (disables grab function), negative values push agents away\n", conf::GRAB_PRESSURE); - fprintf(cf, "SOUNDPITCHRANGE= %f \t//range below hearhigh and above hearlow within which external sounds fade in. Would not recommend extreme values near or beyond [0,0.5]\n", conf::SOUNDPITCHRANGE); + fprintf(cf, "GRAVITY_ACCELERATION= %f \t//how fast an agent will 'fall' after jumping. 0= jump disabled, 0.1+ = super-gravity\n", conf::GRAVITY_ACCELERATION); + fprintf(cf, "BUMP_PRESSURE= %f \t//the restoring force between two colliding agents. 0= no reaction (disables all collisions). I'd avoid negative values if I were you...\n", conf::BUMP_PRESSURE); + fprintf(cf, "GRAB_PRESSURE= %f \t//the restoring force between and agent and its grab target. 0= no reaction (disables grab function), negative values make grabbing agents push others away instead\n", conf::GRAB_PRESSURE); + fprintf(cf, "SOUND_PITCH_RANGE= %f \t//range below hearhigh and above hearlow within which external sounds fade in. Would not recommend extreme values near or beyond [0,0.5]\n", conf::SOUND_PITCH_RANGE); fprintf(cf, "\n"); - fprintf(cf, "BRAINSIZE= %i \t\t\t//number boxes in every agent brain. Sim will NEVER make brains smaller than # Inputs + # Outputs. Saved per world, loaded worlds will override this value. You cannot change this value for worlds already in progress; use New World options or restart app\n", conf::BRAINSIZE); + fprintf(cf, "BRAINBOXES= %i \t\t\t//number boxes in every agent brain. Note: the sim will NEVER make brains smaller than # of Outputs. Saved per world, loaded worlds will override this value. You cannot change this value for worlds already in progress; either use New World options or restart\n", conf::BRAINBOXES); + fprintf(cf, "BRAINCONNS= %i \t\t//number connections attempted for every init agent brain. Sim may make brains with less than this number, due to trimming. Changing this value will only effect newly spawned agents. Default= %i\n", conf::BRAINCONNS, conf::BRAINCONNS); fprintf(cf, "WHEEL_SPEED= %f \t\t//fastest possible speed of agents. This effects so much of the sim I dont advise changing it\n", conf::WHEEL_SPEED); fprintf(cf, "MEANRADIUS= %f \t\t//\"average\" agent radius, range [0.2*this,2.2*this) (only applies to random agents, no limits on mutations). This effects SOOOO much stuff, and I would not recommend setting negative unless you like crashing programs.\n", conf::MEANRADIUS); - fprintf(cf, "BOOSTSIZEMULT= %f \t//how much speed boost do agents get when boost is active?\n", conf::BOOSTSIZEMULT); - fprintf(cf, "BOOSTEXAUSTMULT= %f \t//how much exhaustion from brain outputs is multiplied by when boost is active?\n", conf::BOOSTEXAUSTMULT); - fprintf(cf, "BASEEXHAUSTION= %f \t//base value of exhaustion. When negative, is essentially the sum amount of output allowed before healthloss. Would not recommend >=0 values\n", conf::BASEEXHAUSTION); - fprintf(cf, "EXHAUSTION_MULT= %f \t//multiplier applied to outputsum + BASEEXHAUSTION\n", conf::EXHAUSTION_MULT); - fprintf(cf, "MEANRADIUS= %f \t\t//\"average\" agent radius, range [0.2*this,2.2*this) (only applies to random agents, no limits on mutations). This effects SOOOO much stuff, and I would not recommend setting negative unless you like crashing programs.\n", conf::MEANRADIUS); + fprintf(cf, "JUMP_MOVE_BONUS_MULT= %f \t//how much speed boost do agents get when jump is active? This is not the main feature of jumpping. Value of 1 gives no bonus move speed.\n", conf::JUMP_MOVE_BONUS_MULT); + fprintf(cf, "BOOST_MOVE_MULT= %f \t//how much speed boost do agents get when boost is active? Value of 1 gives no bonus move speed.\n", conf::BOOST_MOVE_MULT); + fprintf(cf, "BOOST_EXAUSTION_MULT= %f \t//how much exhaustion from brain is multiplied by when boost is active?\n", conf::BOOST_EXAUSTION_MULT); + fprintf(cf, "BASEEXHAUSTION= %f \t//base value of exhaustion. When negative, is essentially the sum amount of output allowed before healthloss. Would not recommend positive values\n", conf::BASEEXHAUSTION); + fprintf(cf, "EXHAUSTION_MULT_PER_OUTPUT= %f //multiplier applied to the sum value of all outputs; effectively, the cost multiplier of performing actions. Increase to pressure agents to chose their output more carefully\n", conf::EXHAUSTION_MULT_PER_OUTPUT); + fprintf(cf, "EXHAUSTION_MULT_PER_CONN= %f //multiplier applied to the count of brain connections; effectively, the cost multiplier of maintaining each synapse in the brain. Increase to pressure agents to make more efficient brains.\n", conf::EXHAUSTION_MULT_PER_CONN); fprintf(cf, "MAXWASTEFREQ= %i \t\t//max waste frequency allowed for agents. Agents can select [1,this]. Default= 200, 1= no agent control allowed, 0= program crash\n", conf::MAXWASTEFREQ); - fprintf(cf, "FOODTRANSFER= %f \t\t//how much health is transferred between two agents trading food per tick? =0 disables all generosity\n", conf::FOODTRANSFER); + fprintf(cf, "GENEROCITY_RATE= %f \t\t//how much health is transferred between two agents trading food per tick? =0 disables all generosity\n", conf::GENEROCITY_RATE); fprintf(cf, "SPIKESPEED= %f \t\t//how quickly can the spike be extended? Does not apply to retraction, which is instant\n", conf::SPIKESPEED); + fprintf(cf, "SPAWN_MIRROR_EYES= %i \t\t//true-false flag for if random spawn agents have mirrored eyes. 0= asymmetry, 1= symmetry\n", (int)conf::SPAWN_MIRROR_EYES); + fprintf(cf, "PRESERVE_MIRROR_EYES= %i \t//true-false flag for if mutations can break mirrored eye symmetry. 0= mutations do not preserve symmetry, 1= agents always have mirrored eyes\n", (int)conf::PRESERVE_MIRROR_EYES); fprintf(cf, "\n"); fprintf(cf, "TENDERAGE= %i \t\t\t//age (in 1/10ths) of agents where full meat, hazard, and collision damage is finally given. These multipliers are reduced via *age/TENDERAGE. 0= off\n", conf::TENDERAGE); fprintf(cf, "MINMOMHEALTH= %f \t\t//minimum amount of health required for an agent to have a child\n", conf::MINMOMHEALTH); fprintf(cf, "MIN_INTAKE_HEALTH_RATIO= %f //minimum metabolism ratio of intake always sent to health. 0= no restrictions (agent metabolism has full control), 1= 100%% health, no babies ever. default= 0.5\n", conf::MIN_INTAKE_HEALTH_RATIO); fprintf(cf, "REP_PER_BABY= %f \t\t//amount of food required to be consumed for an agent to reproduce, per baby\n", conf::REP_PER_BABY); - fprintf(cf, "OVERHEAL_REPFILL= %i \t\t//true-false flag for letting agents redirect overfill health (>2) to repcounter. 1= conserves matter, 0= extra intake is destroyed, punishing overeating\n", conf::OVERHEAL_REPFILL); + fprintf(cf, "OVERHEAL_REPFILL= %f \t//decimal value flag for letting agents redirect overfill health (>2) to repcounter. 0= extra intake is destroyed, punishing overeating, 1= conserves matter. Can be set to a decimal value to mult the conversion factor\n", conf::OVERHEAL_REPFILL); // fprintf(cf, "LEARNRATE= %f\n", conf::LEARNRATE); fprintf(cf, "\n"); - fprintf(cf, "MAXDEVIATION= %f \t//maximum difference in species ID a crossover reproducing agent will be willing to tolerate\n", conf::MAXDEVIATION); - fprintf(cf, "DEFAULT_MUTCHANCE= %f \t//the default chance of mutations occurring (note that various mutations modify this value up or down)\n", conf::DEFAULT_MUTCHANCE); - fprintf(cf, "DEFAULT_MUTSIZE= %f \t//the default magnitude of mutations (note that various mutations modify this value up or down)\n", conf::DEFAULT_MUTSIZE); + fprintf(cf, "OVERRIDE_KINRANGE= %d \t\t//for the purposes of sexual reproduction and generosity, the kinrange of agents can be overridden to this value. set to -1 to disable forcing any value.\n", conf::OVERRIDE_KINRANGE); + fprintf(cf, "DEFAULT_BRAIN_MUTCHANCE= %f //the default chance of BRAIN mutations occurring (note that various mutations modify this value up or down and this only effects initial agents)\n", conf::DEFAULT_BRAIN_MUTCHANCE); + fprintf(cf, "DEFAULT_BRAIN_MUTSIZE= %f //the default magnitude of BRAIN mutations (note that various mutations modify this value up or down and this only effects initial agents)\n", conf::DEFAULT_BRAIN_MUTSIZE); + fprintf(cf, "DEFAULT_GENE_MUTCHANCE= %f //the default chance of GENE mutations occurring (note that various mutations modify this value up or down and this only effects initial agents)\n", conf::DEFAULT_GENE_MUTCHANCE); + fprintf(cf, "DEFAULT_GENE_MUTSIZE= %f \t//the default magnitude of GENE mutations (note that various mutations modify this value up or down and this only effects initial agents)\n", conf::DEFAULT_GENE_MUTSIZE); fprintf(cf, "LIVE_MUTATE_CHANCE= %f \t//chance, per tick, that a given agent will be mutated alive. Not typically harmful. Can be increased by mutation events if enabled.\n", conf::LIVE_MUTATE_CHANCE); fprintf(cf, "\n"); - fprintf(cf, "DIST= %f \t\t//how far the senses can detect other agents or cells\n", conf::DIST); - fprintf(cf, "SPIKELENGTH= %f \t\t//full spike length. Should not be more than DIST. 0 disables interaction\n", conf::SPIKELENGTH); - fprintf(cf, "TOOCLOSE= %f \t\t//how much two agents can be overlapping before they take damage. 0 disables interaction\n", conf::TOOCLOSE); - fprintf(cf, "FOOD_SHARING_DISTANCE= %f //how far away can food be shared between agents? Should not be more than DIST. 0 disables interaction\n", conf::FOOD_SHARING_DISTANCE); - fprintf(cf, "SEXTING_DISTANCE= %f \t//how far away can two agents sexually reproduce? Should not be more than DIST. 0 disables interaction\n", conf::SEXTING_DISTANCE); - fprintf(cf, "GRABBING_DISTANCE= %f \t//how far away can an agent grab another? Should not be more than DIST. 0 disables interaction\n", conf::GRABBING_DISTANCE); + fprintf(cf, "MAX_SENSORY_DISTANCE= %f //old DIST, how far the senses can detect other agents or cells\n", conf::MAX_SENSORY_DISTANCE); + fprintf(cf, "SPIKE_LENGTH= %f \t//full spike length. Should not be more than MAX_SENSORY_DISTANCE. 0 disables interaction\n", conf::SPIKE_LENGTH); + fprintf(cf, "BITE_DISTANCE= %f \t//distance that the bite feature ranges out beyond agent radius. Should not be more than MAX_SENSORY_DISTANCE. 0 disables interaction & rendering\n", conf::BITE_DISTANCE); + fprintf(cf, "BUMP_DAMAGE_OVERLAP= %f \t//how much two agents can be overlapping before they take damage. 0 disables interaction\n", conf::BUMP_DAMAGE_OVERLAP); + fprintf(cf, "FOOD_SHARING_DISTANCE= %f //how far away can food be shared between agents? Should not be more than MAX_SENSORY_DISTANCE. 0 disables interaction\n", conf::FOOD_SHARING_DISTANCE); + fprintf(cf, "SEXTING_DISTANCE= %f \t//how far away can two agents sexually reproduce? Should not be more than MAX_SENSORY_DISTANCE. 0 disables interaction\n", conf::SEXTING_DISTANCE); + fprintf(cf, "GRABBING_DISTANCE= %f \t//how far away can an agent grab another? Should not be more than MAX_SENSORY_DISTANCE. 0 disables interaction\n", conf::GRABBING_DISTANCE); fprintf(cf, "\n"); fprintf(cf, "FRESHKILLTIME= %i \t\t//number of ticks after a spike, collision, or bite that an agent will still drop full meat\n", conf::FRESHKILLTIME); - fprintf(cf, "CORPSE_FRAMES= %i \t//number of frames before dead agents are removed after meat= CORPSE_MEAT_MIN\n", conf::CORPSE_FRAMES); + fprintf(cf, "CORPSE_FRAMES= %i \t\t//number of frames before dead agents are removed after meat= CORPSE_MEAT_MIN\n", conf::CORPSE_FRAMES); fprintf(cf, "CORPSE_MEAT_MIN= %f \t//minimum amount of meat on cell under dead agent before the agent starts counting down from CORPSE_FRAMES\n", conf::CORPSE_MEAT_MIN); fprintf(cf, "\n"); fprintf(cf, "MAXAGE= %i \t\t\t//Age at which the full HEALTHLOSS_AGING amount is applied to an agent\n", conf::MAXAGE); - fprintf(cf, "HEALTHLOSS_AGING= %f \t//health lost at MAXAGE. Note that this damage is applied to all agents in proportion to their age.\n", conf::HEALTHLOSS_AGING); - fprintf(cf, "HEALTHLOSS_WHEELS= %f \t//How much health is lost for an agent driving at full speed\n", conf::HEALTHLOSS_WHEELS); - fprintf(cf, "HEALTHLOSS_BOOSTMULT= %f \t//how much boost costs (set to 1 to nullify boost cost; its a multiplier)\n", conf::HEALTHLOSS_BOOSTMULT); - fprintf(cf, "HEALTHLOSS_BADTEMP= %f \t//how quickly health drains in non-preferred temperatures\n", conf::HEALTHLOSS_BADTEMP); - fprintf(cf, "HEALTHLOSS_BRAINUSE= %f \t//how much health is reduced for each box in the brain being active\n", conf::HEALTHLOSS_BRAINUSE); - fprintf(cf, "HEALTHLOSS_SPIKE_EXT= %f \t//how much health an agent looses for extending spike\n", conf::HEALTHLOSS_SPIKE_EXT); - fprintf(cf, "HEALTHLOSS_BADTERRAIN= %f //how much health is lost if in totally opposite environment\n", conf::HEALTHLOSS_BADTERRAIN); - fprintf(cf, "HEALTHLOSS_NOOXYGEN= %f \t//how much agents are penalized when total agents = AGENTS_MAX_NOOXYGEN\n", conf::HEALTHLOSS_NOOXYGEN); - fprintf(cf, "HEALTHLOSS_ASSEX= %f \t//multiplier for radius/MEANRADIUS penalty on mother for asexual reproduction\n", conf::HEALTHLOSS_ASSEX); + fprintf(cf, "HEALTHLOSS_AGING= %f \t//health lost at MAXAGE. Note that this damage is applied to all agents in proportion to their age. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_AGING, conf::DEATH_NATURAL); + fprintf(cf, "HEALTHLOSS_EXHAUSTION= %f //health lost from exhaustion squared. As agents get more exhausted, they loose more health. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_AGING, conf::DEATH_NATURAL); + fprintf(cf, "HEALTHLOSS_WHEELS= %f \t//How much health is lost for an agent driving at full speed. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_WHEELS, conf::DEATH_NATURAL); + fprintf(cf, "HEALTHLOSS_BOOSTMULT= %f \t//how much boost costs (its a multiplier; set to 1 to nullify boost cost). Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_BOOSTMULT, conf::DEATH_NATURAL); + fprintf(cf, "HEALTHLOSS_BRAINUSE= %f \t//how much health is reduced for each box in the brain being active. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_BRAINUSE, conf::DEATH_NATURAL); + fprintf(cf, "HEALTHLOSS_BADTEMP= %f \t//how quickly health drains in non-preferred temperatures. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_BADTEMP, conf::DEATH_BADTEMP); + fprintf(cf, "HEALTHLOSS_SPIKE_EXT= %f \t//how much health an agent looses for extending spike. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_SPIKE_EXT, conf::DEATH_SPIKERAISE); + fprintf(cf, "HEALTHLOSS_BADTERRAIN= %f //how much health is lost if in totally opposite environment. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_BADTERRAIN, conf::DEATH_BADTERRAIN); + fprintf(cf, "HEALTHLOSS_NOOXYGEN= %f \t//how much agents are penalized when total agents = AGENTS_MAX_NOOXYGEN. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_NOOXYGEN, conf::DEATH_TOOMANYAGENTS); + fprintf(cf, "HEALTHLOSS_ASSEX= %f \t//multiplier for radius/MEANRADIUS penalty on mother for asexual reproduction. Contributes to the death cause '%s'.\n", conf::HEALTHLOSS_ASSEX, conf::DEATH_ASEXUAL); fprintf(cf, "\n"); - fprintf(cf, "DAMAGE_FULLSPIKE= %f \t//health multiplier of spike injury. Note: it is effected by spike length and relative velocity of the agents\n", conf::DAMAGE_FULLSPIKE); - fprintf(cf, "DAMAGE_COLLIDE= %f \t//how much health is lost upon collision. Note that =0 does not disable the event (see TOOCLOSE above)\n", conf::DAMAGE_COLLIDE); - fprintf(cf, "DAMAGE_JAWSNAP= %f \t//when jaws snap fully (0->1), this is the damage applied to agents in front\n", conf::DAMAGE_JAWSNAP); + fprintf(cf, "DAMAGE_FULLSPIKE= %f \t//health multiplier of spike injury. Note: it is effected by spike length and relative velocity of the agents. When used against another agent, it causes the death cause '%s'.\n", conf::DAMAGE_FULLSPIKE, conf::DEATH_SPIKE); + fprintf(cf, "DAMAGE_COLLIDE= %f \t//how much health is lost upon collision. Note that =0 does not disable the event (see BUMP_DAMAGE_OVERLAP above). When used against another agent, it causes the death cause '%s'.\n", conf::DAMAGE_COLLIDE, conf::DEATH_COLLIDE); + fprintf(cf, "DAMAGE_JAWSNAP= %f \t//when jaws snap fully (0->1), this is the damage applied to agents in front. When used against another agent, it causes the death cause '%s'.\n", conf::DAMAGE_JAWSNAP, conf::DEATH_BITE); fprintf(cf, "\n"); - fprintf(cf, "STOMACH_EFF= %f \t\t//the worst possible multiplier produced from having at least two stomach types at 1. =0.1 is harsh. =1 disables (always full efficiency)\n", conf::STOMACH_EFF); + fprintf(cf, "STOMACH_EFFICIENCY= %f \t//the worst possible multiplier produced from having at least two stomach types at 1. =0.1 is harsh. =1 disables (always full efficiency)\n", conf::STOMACH_EFFICIENCY); fprintf(cf, "\n"); - fprintf(cf, "FOODINTAKE= %f \t\t//how much plant food can feed an agent per tick?\n", conf::FOODINTAKE); - fprintf(cf, "FOODDECAY= %f \t\t//how much does food decay per tick? (negative values make it grow everywhere instead)\n", conf::FOODDECAY); - fprintf(cf, "FOODGROWTH= %f \t\t//how much does food increase by on a cell with both plant and hazard? (fertilizer effect). =0 disables\n", conf::FOODGROWTH); - fprintf(cf, "FOODWASTE= %f \t\t//how much food disappears when an agent eats it?\n", conf::FOODWASTE); - fprintf(cf, "FOODADDFREQ= %i \t\t//how often does a random cell get set to full food randomly? Lower values are more frequent\n", conf::FOODADDFREQ); - fprintf(cf, "FOODSPREAD= %f \t\t//probability of a plant cell being seeded to a nearby cell. 0.0002= VERY fast food growth\n", conf::FOODSPREAD); - fprintf(cf, "FOODRANGE= %i \t\t\t//distance that a single cell of food can seed. Units in cells.\n", conf::FOODRANGE); + fprintf(cf, "PLANT_INTAKE= %f \t\t//how much plant food can feed an agent per tick?\n", conf::PLANT_INTAKE); + fprintf(cf, "PLANT_DECAY= %f \t\t//how much does food decay per tick? (negative values make it grow everywhere instead)\n", conf::PLANT_DECAY); + fprintf(cf, "PLANT_GROWTH= %f \t\t//how much does food increase by on a cell with both plant and hazard? (fertilizer effect). =0 disables\n", conf::PLANT_GROWTH); + fprintf(cf, "PLANT_WASTE= %f \t\t//how much food disappears when an agent eats it?\n", conf::PLANT_WASTE); + fprintf(cf, "PLANT_ADD_FREQ= %i \t\t//how often does a random cell get set to full food randomly? Lower values are more frequent\n", conf::PLANT_ADD_FREQ); + fprintf(cf, "PLANT_SPREAD= %f \t\t//probability of a plant cell being seeded to a nearby cell. 0.0002= VERY fast food growth\n", conf::PLANT_SPREAD); + fprintf(cf, "PLANT_RANGE= %i \t\t\t//distance that a single cell of food can seed. Units in cells.\n", conf::PLANT_RANGE); + fprintf(cf, "PLANT_TENACITY= %f \t//Plant Tenacity is an effect where the amount of plant food taken is multiplied by the amount of plant food itself, where this value is the minimum allowed mult. Helps prevent plant cells from completely dieing. Set to 1 to disable tenacity effect, set to 0 to make herbivores never finish meals.\n", conf::PLANT_TENACITY); fprintf(cf, "\n"); - fprintf(cf, "FRUITINTAKE= %f \t\t//how much fruit can feed an agent per tick?\n", conf::FRUITINTAKE); - fprintf(cf, "FRUITDECAY= %f \t\t//how much fruit decays per tick? This is applied *2 if less than FRUITREQUIRE plant is in the cell\n", conf::FRUITDECAY); - fprintf(cf, "FRUITWASTE= %f \t\t//how much fruit disappears when an agent eats it?\n", conf::FRUITWASTE); - fprintf(cf, "FRUITADDFREQ= %i \t\t//how often does a high-plant-food cell get set to full fruit? Higher values are less frequent (must have at least FRUITREQUIRE plant)\n", conf::FRUITADDFREQ); - fprintf(cf, "FRUITREQUIRE= %f \t\t//minimum plant food on same cell required for fruit to persist or populate\n", conf::FRUITREQUIRE); + fprintf(cf, "FRUIT_INTAKE= %f \t\t//how much fruit can feed an agent per tick?\n", conf::FRUIT_INTAKE); + fprintf(cf, "FRUIT_DECAY= %f \t\t//how much fruit decays per tick? This is applied *2 if less than FRUIT_PLANT_REQUIRE plant is in the cell\n", conf::FRUIT_DECAY); + fprintf(cf, "FRUIT_WASTE= %f \t\t//how much fruit disappears when an agent eats it?\n", conf::FRUIT_WASTE); + fprintf(cf, "FRUIT_ADD_FREQ= %i \t\t//how often does a high-plant-food cell get set to full fruit? Higher values are less frequent (must have at least FRUIT_PLANT_REQUIRE plant)\n", conf::FRUIT_ADD_FREQ); + fprintf(cf, "FRUIT_PLANT_REQUIRE= %f \t//minimum plant food on same cell required for fruit to persist or populate\n", conf::FRUIT_PLANT_REQUIRE); fprintf(cf, "\n"); - fprintf(cf, "MEATINTAKE= %f \t\t//how much meat can feed an agent per tick?\n", conf::MEATINTAKE); - fprintf(cf, "MEATDECAY= %f \t\t//how much meat decays on a cell per tick? (negative values make it grow everywhere instead)\n", conf::MEATDECAY); - fprintf(cf, "MEATWASTE= %f \t\t//how much meat disappears when an agent eats it?\n", conf::MEATWASTE); - fprintf(cf, "MEATVALUE= %f \t\t//how much meat an agent's body is worth?\n", conf::MEATVALUE); + fprintf(cf, "MEAT_INTAKE= %f \t\t//how much meat can feed an agent per tick?\n", conf::MEAT_INTAKE); + fprintf(cf, "MEAT_DECAY= %f \t\t//how much meat decays on a cell per tick? (negative values make it grow everywhere instead)\n", conf::MEAT_DECAY); + fprintf(cf, "MEAT_WASTE= %f \t\t//how much meat disappears when an agent eats it?\n", conf::MEAT_WASTE); + fprintf(cf, "MEAT_VALUE= %f \t\t//how much meat an agent's body is worth? Typically, and agent > TENDERAGE, with no carnivore stomach, and was freshly killed, will fill a meat cell. Not being a fresh kill multiplies the amount by MEAT_NON_FRESHKILL_MULT. Being a full carnivore, dramatic reduction. And < TENDERAGE will multiply the amount by the proportion until they are TENDERAGE. This multiplies in after all that.\n", conf::MEAT_VALUE); + fprintf(cf, "MEAT_NON_FRESHKILL_MULT= %f //mult for when the agent's death was not due to an attack (died by attrition means). 0= agents killed by other means never drop any meat, 1= no reduction from meat dropped.\n", conf::MEAT_NON_FRESHKILL_MULT); fprintf(cf, "\n"); - fprintf(cf, "HAZARDFREQ= %i \t\t\t//how often an instant hazard appears?\n", conf::HAZARDFREQ); - fprintf(cf, "HAZARDEVENT_MULT= %f \t//multiplier for agents standing in a cell with a hazard event ongoing. multipied after HAZARDDAMAGE\n", conf::HAZARDEVENT_MULT); - fprintf(cf, "HAZARDDECAY= %f \t\t//how much non-event hazard decays on a cell per tick? (negative values make it grow everywhere instead)\n", conf::HAZARDDECAY); - fprintf(cf, "HAZARDDEPOSIT= %f \t//how much hazard is placed by an agent per tick? (Ideally. Agents can control the frequency and amount that they deposit, but in sum it should equal this per tick)\n", conf::HAZARDDEPOSIT); - fprintf(cf, "HAZARDPOWER= %f \t\t//power of the hazard layer value, applied before HAZARDDAMAGE. default= 0.5 (remember, hazard in range [0,1])\n", conf::HAZARDPOWER); - fprintf(cf, "HAZARDDAMAGE= %f \t\t//how much health an agent looses while on a filled hazard cell per tick? (note that 9/10 of this is max waste damage)\n", conf::HAZARDDAMAGE); + fprintf(cf, "HAZARD_EVENT_FREQ= %i \t\t//how often an instant hazard appears?\n", conf::HAZARD_EVENT_FREQ); + fprintf(cf, "HAZARD_EVENT_MULT= %f \t//multiplier for agents standing in a cell with a hazard event ongoing. multipied after HAZARD_DAMAGE\n", conf::HAZARD_EVENT_MULT); + fprintf(cf, "HAZARD_DECAY= %f \t\t//how much non-event hazard decays on a cell per tick? (negative values make it grow everywhere instead)\n", conf::HAZARD_DECAY); + fprintf(cf, "HAZARD_DEPOSIT= %f \t//how much hazard is placed by an agent per tick? (Ideally. Agents can control the frequency and amount that they deposit, but in sum it should equal this per tick)\n", conf::HAZARD_DEPOSIT); + fprintf(cf, "HAZARD_POWER= %f \t\t//power of the hazard layer value, applied before HAZARD_DAMAGE. default= 0.5 (remember, hazard in range [0,1])\n", conf::HAZARD_POWER); + fprintf(cf, "HAZARD_DAMAGE= %f \t//how much health an agent looses while on a filled hazard cell per tick? (note that 9/10 of this is max waste damage). Contributes to the death cause '%s'.\n", conf::HAZARD_DAMAGE, conf::DEATH_HAZARD); fprintf(cf, "\n"); fprintf(cf, "SUN_RED= %f \t\t//???\n", SUN_RED); fprintf(cf, "SUN_GRE= %f \t\t//???\n", SUN_GRE); diff --git a/SOURCE/World.h b/SOURCE/World.h index 83efcc9..cb809a4 100644 --- a/SOURCE/World.h +++ b/SOURCE/World.h @@ -3,8 +3,8 @@ #include "Agent.h" #include "settings.h" -//#include "ReadWrite.h" #include +#include #include using namespace irrklang; @@ -56,6 +56,7 @@ class World //following and selected agent stuff int getSelectedID() const; + bool isAgentSelected(int id); float pleft; float pright; @@ -65,7 +66,8 @@ class World void setSelection(int type); bool setSelectionRelative(int posneg); void setSelectedAgent(int idx = -1); - int getSelectedAgent() const; + int getSelectedAgentIndex(); + Agent* getSelectedAgent(); int getClosestRelative(int idx = -1); void selectedHeal(); void selectedKill(); @@ -89,17 +91,17 @@ class World bool processMouse(int button, int state, int x, int y, float scale); //graph stuff - int numHerbivore[conf::RECORD_SIZE]; - int numCarnivore[conf::RECORD_SIZE]; - int numFrugivore[conf::RECORD_SIZE]; - int numTerrestrial[conf::RECORD_SIZE]; - int numAmphibious[conf::RECORD_SIZE]; - int numAquatic[conf::RECORD_SIZE]; - int numHybrid[conf::RECORD_SIZE]; - int numDead[conf::RECORD_SIZE]; - int numTotal[conf::RECORD_SIZE]; - int ptr; - int lastptr; + std::vector graphGuides; + std::vector numTotal; + std::vector numDead; + std::vector numHerbivore; + std::vector numCarnivore; + std::vector numFrugivore; + std::vector numTerrestrial; + std::vector numAmphibious; + std::vector numAquatic; + std::vector numHybrid; + std::vector numSpiky; //UNUSED //counters int modcounter; @@ -110,12 +112,24 @@ class World std::vector agents; Agent loadedagent; + void processWorldTick(); //handle all tick/day/report/epoch opperations + void processNewEpoch(); + void processClimate(); + void processMutationEvent(); + void processReporting(); + void triggerStatEvents(bool showevents= true); + void processCells(bool prefire= false); + void tryPlayMusic(); //CONTROL.CPP!!! void setInputs(); void brainsTick(); //takes in[] to out[] for every agent + void processCounters(); //handle most agent counter variables and do other stuff before processOutputs and healthTick void processOutputs(bool prefire= false); //prefire used to run post-load sim restoration before restart - void processInteractions(); //does the final causes of death and interactions with cells and agents - - void healthTick(); + void healthTick(); //process agent health + void processReproduction(); //handle all agent's reproduction needs + void processCellInteractions(); //does interactions of agents with cells + void processAgentInteractions(); //does interactions of agents with agents + void processDeath(); //manage the distribution of meat, alerts, and death system functions + void processRandomSpawn(); //handle spawning of random agents; gotta keep the world filled! void addAgent(Agent &agent); bool addLoadedAgent(float x, float y); @@ -130,8 +144,11 @@ class World std::vector > > events; //short-term record of events happening in the sim. includes text, type, and counter void addEvent(std::string text, int type= 0); //adds event to the event list. max of ~40 characters + void addTipEvent(std::string text, int type, int agentId); //post an event only if we: A: are in demo mode, and B: have the current agent selected that matches the index passed void dismissNextEvents(int count= 1); //dismisses next [count] events (starts their disappearing act, doesn't delete them!) + void setStatsAfterLoad(); + //helper functions to give us counts of agents and cells int getHerbivores() const; int getCarnivores() const; @@ -162,6 +179,13 @@ class World float cells[Layer::LAYERS][conf::WIDTH/conf::CZ][conf::HEIGHT/conf::CZ]; //[LAYER][CELL_X][CELL_Y] std::vector > Tcells[Layer::LAYERS]; //test cell layer: array of 2-d vectors. Array portion is immutable layer data. 2-d size is adjustable + //public stats + int STAThighestgen; //highest and lowest generation (excluding gen 0 unless that's all there is) + int STATlowestgen; + float STATinvgenrange; //range of generation values, with high-gen forcast, inverted (1/this) + int MINPOP; + int MAXPOP; + bool MAXAFTERMIN; //if max is set after min (pop rise), this returns true, otherwise false (pop fall) //reloadable "constants" int MIN_PLANT; @@ -188,6 +212,9 @@ class World float OCEANPERCENT; bool SPAWN_LAKES; bool DISABLE_LAND_SPAWN; + float AMBIENT_LIGHT_PERCENT; + bool AGENTS_SEE_CELLS; + bool AGENTS_DONT_OVERDONATE; bool MOONLIT; float MOONLIGHTMULT; //saved multiplier of the desired moonlight mult bool DROUGHTS; @@ -198,29 +225,37 @@ class World int MUTEVENTMULT; //saved multiplier of the current epoch mutation chance & count multiplier (min always 1) int MUTEVENT_MAX; bool CLIMATE; - bool CLIMATE_KILL_FLORA; + bool CLIMATE_AFFECT_FLORA; float CLIMATEBIAS; //saved bias of the current epoch climate state. This can push the entire planet towards 0 or 1 temperature float CLIMATEMULT; //saved multiplier of the current epoch climate state. This can blend the poles and equator closer to CLIMATEBIAS float CLIMATEMULT_AVERAGE; float CLIMATE_INTENSITY; - float GRAVITYACCEL; + float GRAVITY_ACCELERATION; float BUMP_PRESSURE; float GRAB_PRESSURE; - int BRAINSIZE; + int BRAINBOXES; + int BRAINCONNS; float WHEEL_SPEED; - float BOOSTSIZEMULT; - float BOOSTEXAUSTMULT; + float JUMP_MOVE_BONUS_MULT; + float BOOST_MOVE_MULT; + float BOOST_EXAUSTION_MULT; int CORPSE_FRAMES; float CORPSE_MEAT_MIN; - float SOUNDPITCHRANGE; + float SOUND_PITCH_RANGE; + float INV_SOUND_PITCH_RANGE; - float FOODTRANSFER; - float BASEEXHAUSTION; - float EXHAUSTION_MULT; float MEANRADIUS; + float GENEROCITY_RATE; + float BASEEXHAUSTION; + float EXHAUSTION_MULT_PER_OUTPUT; + float EXHAUSTION_MULT_PER_CONN; float SPIKESPEED; + bool SPAWN_MIRROR_EYES; + bool PRESERVE_MIRROR_EYES; + int FRESHKILLTIME; int TENDERAGE; + float INV_TENDERAGE; float MINMOMHEALTH; float MIN_INTAKE_HEALTH_RATIO; bool FUN; @@ -230,16 +265,20 @@ class World float REP_PER_BABY; float OVERHEAL_REPFILL; // float LEARNRATE; - float MAXDEVIATION; - float DEFAULT_MUTCHANCE; - float DEFAULT_MUTSIZE; + int OVERRIDE_KINRANGE; + float DEFAULT_BRAIN_MUTCHANCE; + float DEFAULT_BRAIN_MUTSIZE; + float DEFAULT_GENE_MUTCHANCE; + float DEFAULT_GENE_MUTSIZE; float LIVE_MUTATE_CHANCE; int MAXAGE; int MAXWASTEFREQ; - float DIST; - float SPIKELENGTH; - float TOOCLOSE; + float MAX_SENSORY_DISTANCE; + float INV_MAX_SENSORY_DISTANCE; + float SPIKE_LENGTH; + float BITE_DISTANCE; + float BUMP_DAMAGE_OVERLAP; float FOOD_SHARING_DISTANCE; float SEXTING_DISTANCE; float GRABBING_DISTANCE; @@ -249,6 +288,7 @@ class World float HEALTHLOSS_BOOSTMULT; float HEALTHLOSS_BADTEMP; float HEALTHLOSS_AGING; + float HEALTHLOSS_EXHAUSTION; float HEALTHLOSS_BRAINUSE; float HEALTHLOSS_SPIKE_EXT; float HEALTHLOSS_BADTERRAIN; @@ -259,40 +299,42 @@ class World float DAMAGE_COLLIDE; float DAMAGE_JAWSNAP; - float STOMACH_EFF; - - float FOODINTAKE; - float FOODDECAY; - float FOODGROWTH; - float FOODWASTE; - int FOODADDFREQ; - float FOODSPREAD; - int FOODRANGE; - - float FRUITINTAKE; - float FRUITDECAY; - float FRUITWASTE; - int FRUITADDFREQ; - float FRUITREQUIRE; - - float MEATINTAKE; - float MEATDECAY; - float MEATWASTE; - float MEATVALUE; - - int HAZARDFREQ; - float HAZARDEVENT_MULT; - float HAZARDDECAY; - float HAZARDDEPOSIT; - float HAZARDDAMAGE; - float HAZARDPOWER; + float STOMACH_EFFICIENCY; + + float PLANT_INTAKE; + float PLANT_DECAY; + float PLANT_GROWTH; + float PLANT_WASTE; + int PLANT_ADD_FREQ; + float PLANT_SPREAD; + int PLANT_RANGE; + float PLANT_TENACITY; + + float FRUIT_INTAKE; + float FRUIT_DECAY; + float FRUIT_WASTE; + int FRUIT_ADD_FREQ; + float FRUIT_PLANT_REQUIRE; + + float MEAT_INTAKE; + float MEAT_DECAY; + float MEAT_WASTE; + float MEAT_VALUE; + float MEAT_NON_FRESHKILL_MULT; + + int HAZARD_EVENT_FREQ; + float HAZARD_EVENT_MULT; + float HAZARD_DECAY; + float HAZARD_DEPOSIT; + float HAZARD_DAMAGE; + float HAZARD_POWER; std::vector tips;//list of tips to display every once in a while (more frequently at epoch=0) private: void writeReport(); - void reproduce(int ai, int bi); + void reproduce(int ai, int bi); void cellsRandomFill(int layer, float amount, int number); void cellsLandMasses(); @@ -334,6 +376,8 @@ class World float STATallmeat; //exact number sum of all plant matter float STATallhazard; //exact number sum of all plant matter + bool STATuserseengenerosity; //true if the user was shown an event when an agent sent health via generosity + bool STATuserseenjumping; //true if the user has seen jumping bool STATuseracted; //true if the user took control of an agent bool STATfirstspecies; //true if we have had at least one agent with a generation =5 during a report bool STATfirstpopulation; //true if we had a population count of > AGENTS_MAX_SPAWN diff --git a/SOURCE/helpers.h b/SOURCE/helpers.h index 70383d3..529a86b 100644 --- a/SOURCE/helpers.h +++ b/SOURCE/helpers.h @@ -2,8 +2,18 @@ #define HELPERS_H #include #include +#include #include "vmath.h" +inline float expnf(float x, int n = 8) { + x = 1.0 + x / pow(2.0f, n); + while (n>=0) { + x *= x; + n--; + } + return x; +} + //uniform random in [a,b) inline float randf(float a, float b){return ((b-a)*((float)rand()/RAND_MAX))+a;} @@ -57,6 +67,13 @@ inline float pointline(Vector2f posA, Vector2f posB, Vector2f posC){ return fabs((posC.x-posA.x)*(posB.y-posA.y) - (posC.y-posA.y)*(posB.x-posA.x))/normalLength; } +//delete the first item in a vector, treating it like a queue +template +void pop_front(std::vector& v) +{ + assert(!v.empty()); + v.erase(v.begin()); +} //Passed arrays store different data types template diff --git a/SOURCE/main.cpp b/SOURCE/main.cpp index b6c7ffb..a302e59 100644 --- a/SOURCE/main.cpp +++ b/SOURCE/main.cpp @@ -63,7 +63,7 @@ int main(int argc, char **argv) { //GLUT SETUP glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE | GLUT_RGBA); - glutInitWindowPosition(250,20); + glutInitWindowPosition(260,20); glutInitWindowSize(conf::WWIDTH,conf::WHEIGHT); GLVIEW->win1= glutCreateWindow("Evagents"); diff --git a/SOURCE/settings.h b/SOURCE/settings.h index ee0edc3..a26f474 100644 --- a/SOURCE/settings.h +++ b/SOURCE/settings.h @@ -3,22 +3,20 @@ #include #define NUMEYES 6 -#define NUMEARS 2 -#define CONNS 5 -#define STACKS 3 +#define NUMEARS 4 #ifndef SETTINGS_H #define SETTINGS_H //defines for GUI button handles. Order changes nothing -namespace GUIButtons{ +namespace GUIButtons { const enum { TOGGLE_LAYERS= 0, TOGGLE_PLACEAGENTS };}; //defines for reading/writing GUI handles. Order changes nothing -namespace RWOpen{ +namespace RWOpen { const enum { STARTLOAD= 0, NEWWORLD, @@ -36,7 +34,7 @@ const enum { };}; //defines for reading/writing GUI handles. Order changes nothing -namespace RWClose{ +namespace RWClose { const enum { BASICLOAD= 0, BASICSAVE, @@ -56,58 +54,110 @@ const enum { };}; //defines for tile (buttons) data. Order changes nothing except my sanity -namespace MainTiles{ +namespace MainTiles { const enum { - SAD= 0, //Selected Agent Display, our first tile! It has lots of sub-tiles that get made automatically - TOOLBOX, + LAD= 0, //Selected Agent Display, our first tile! It has lots of sub-tiles that get created automatically + TOOLBOX, //UNUSED, will hold many of the GLUI functions eventually + EVENTS, //UNUSED, will eventually migrate events to GLView and handle them as tiles //Don't add beyond this entry! TILES };}; //defines for ui dimensions and useful values throughout the program -namespace UID{ +namespace UID { const int CHARHEIGHT= 9; - const int CHARWIDTH= 7; //height and width (on screen) allowed for text characters - const int HUDSWIDTH= 88; //the width we are assuming when it comes to text space in the Selected Agent Display. + const int CHARWIDTH= 6; //height and width (on screen) allowed for text characters + const int HUDSWIDTH= 88; //the column width we are assuming when it comes to text space in the Selected Agent Display. const int TILEMARGIN= 10; //what is the min margin between a sub-tile and the next tile above (or window if main tile) - const int TILEBEVEL= 5; //bevel size on tiles. 5 was scaled for the SAD; consider code for smaller tiles to have smaller bevels - const int SADWIDTH= 440; //Selected Agent Display width. SHOULD be tied to HUDSWIDTH above, but there's a lot more stuff going on in there + const int TILEBEVEL= 5; //bevel size on tiles. 5 was scaled for the LAD; consider code for smaller tiles to have smaller bevels + const int TINYTILEWIDTH= 20; //the size of tiny tiles + const int LADWIDTH= 440; //Loaded / seLected Agent Display width. SHOULD be tied to HUDSWIDTH above, but there's a lot more stuff going on in there const int BUFFER= 6; //extra buffer space, mostly for text const int EVENTSWIDTH= 210; //Event toast width (length?)... x-dimension-size + const int GRAPHWIDTH = 10020; //x-d width of the on-world graph + const int GRAPHBUFFER = 15; }; +//defines for the graph's vertical guide lines, indicating days and epochs (or nothing). Order changes nothing +namespace GuideLine { +const enum { + NONE = 0, + DAY, + EPOCH +};}; -namespace EventColor{ +namespace EventColor { const enum { - WHITE= 0, //type 0: white - Selected agent event - RED, //type 1: red - Selected agent killed / population bad - GREEN, //type 2: green - population good - BLUE, //type 3: blue - Selected sexual reproduction - YELLOW, //type 4: yellow - tips / autoselect changed poorly - CYAN, //type 5: cyan - simulation notification and controls, global ice age - PURPLE, //type 6: purple - Selected mutation event - BROWN, //type 7: brown - Selected decayed - NEON, //type 8: neon - Selected assexual reproduction - MINT, //type 9: mint - simulation start/restart/change - MULTICOLOR, //type 10: multicolor -achievements - PINK, //type 11: pink - global change - ORANGE, //type 12: orange - Selected attacked, global hadean or extreme climate - BLOOD, //type 13: bloodred - selected bad - LAVENDER, //type 14: lavender - global change (mutations) - LIME, //type 15: lime - global good - BLACK //type 16+: black - + // Default: - Demo Only: + WHITE= 0, // simulation notification - agent event + RED, // global population decrease - agent killed + GREEN, // global population increase - agent generosity + BLUE, // neutral population change - agent sexual reproduction + YELLOW, // - tips, agent jumped, agent bit another + CYAN, // control help, global ice age - agent collision, grab + PURPLE, // - agent mutation + BROWN, // - agent decayed + NEON, // - agent assexual reproduction + MINT, // simulation start/restart/change + PINK, // global change (overgrowth/drought bad) + ORANGE, // global hadean or extreme climate - agent stabbed another + BLOOD, // load agent failed - agent was stabbed or bitten (non-fatal) + LAVENDER, // global change (mutations) + LIME, // global change (overgrowth/drought good) + MULTICOLOR, // achievements ONLY + BLACK // epoch count, multi-use, Autoselect changed };}; -//defines for mouse modes. Order changes nothing. NOT USED CURRENTLY +//defines for mouse modes. Order changes nothing. namespace MouseMode{ const enum { NONE= 0, - POPUPS, //show popups only SELECT, //select agent only - POPUPS_SELECT, //DEFAULT select agents and show popups - PLACE, //place agents only - POPUPS_PLACE, //show popups and place agents + PLACE_AGENT, //place agents only + PLACE_VALUE, //place cell value to cell layer + + //Don't add beyond this entry! + MODES +};}; + +//defines for LAD (Loaded Agent Display) visual modes. Order changes nothing. +namespace LADVisualMode{ +const enum { + NONE= 0, + GHOST, //draw a ghost agent, copying all motion and events + DAMAGES, //draw the damage amounts pie chart + INTAKES, //draw the cell intake amounts pie chart + + //Don't add beyond this entry! + MODES +};}; + +//defines for LAD (Loaded Agent Display) body modes. Order changes nothing. CURRENTLY UNUSED +namespace LADBodyMode{ +const enum { + NONE= 0, + COMMON, //show traits and stats that are commonly looked at + TRAITS_ONLY, //only show traits (fixed values), and more of them + STATS_ONLY, //only show stats (changing values), and more of them + PROFILE, //display the selected agent's profile + + //Don't add beyond this entry! + MODES +};}; + +//defines for read-write modes. Changing order does nothing +namespace ReadWriteMode{ +const enum { + OFF = 0, + READY, + WORLD, + CELL, + AGENT, + BOX, + CONN, + EYE, + EAR, //Don't add beyond this entry! MODES @@ -146,8 +196,8 @@ namespace DisplayLayer{ const enum { ELEVATION, PLANTS, - MEATS, FRUITS, + MEATS, HAZARDS, TEMP, LIGHT, @@ -163,14 +213,17 @@ const enum { RGB, STOMACH, LUNGS, + DISCOMFORT, HEALTH, - REPCOUNTER, METABOLISM, - DISCOMFORT, + REPCOUNTER, + STRENGTH, VOLUME, + BRAINMUTATION, + GENEMUTATION, + GENERATIONS, SPECIES, CROSSABLE, -// REPMODE, //Don't add beyond this entry! VISUALS @@ -208,7 +261,7 @@ const enum { STATICDISPLAYS };}; -//defines for global agent stats display in the data region. Changing order here changes arrangement order +//defines for global agent stats display in the data region. Changing order here changes arrangement order. UNUSED namespace DataDisplay{ const enum { blank1= 0, @@ -240,7 +293,7 @@ const enum { //defines for selected agent heads up display ordering. Changing order here changes arrangement order //keep in mind, it is displayed in 3 columns, so the 4th entry will be in the same column as the 1st, //5th with the 2nd, etc -namespace SADHudOverview{ +namespace LADHudOverview{ const enum { HEALTH= 0, REPCOUNTER, @@ -269,10 +322,11 @@ const enum { TONE, STAT_KILLED, STAT_CHILDREN, - MUTCHANCE, - MUTSIZE, - blank2, - DEATH, + BRAINMUTCHANCE, + BRAINMUTSIZE, + BRAINSIZE, + GENEMUTCHANCE, + GENEMUTSIZE, //Don't add beyond this entry! HUDS @@ -282,7 +336,7 @@ const enum { //keep in mind, it is displayed in 3 columns, so the 4th entry will be in the same column as the 1st, //5th with the 2nd, etc //these are STATS, so they can CHANGE! -namespace SADHudStats{ +namespace LADHudStats{ const enum { HEALTH= 0, REPCOUNTER, @@ -324,18 +378,38 @@ const enum { SELECT_TYPES };}; +//defines for reproduction modes. I don't forsee any reason to add more or change their order; it effects no visuals, except color (for now) +namespace RepType { +const enum { + ASEXUAL, + FEMALE, + MALE +};}; + +//defines for sound types. Order changes nothing. +namespace Hearing { +const enum { + VOICE, + MOVEMENT, + //INTAKE, + + //Don't add beyond this entry! + TYPES +};}; + //defines for brain input code. Changing order here changes input-to-brain order and visualization order namespace Input { const enum { EYES, xEYES= EYES+NUMEYES*3-1, //I strongly recommend keeping EYES and xEYES together in this order HEALTH, + REPCOUNT, RANDOM, CLOCK1, CLOCK2, CLOCK3, - HEARING1, - HEARING2, + EARS, + xEARS= EARS+NUMEARS-1, //same comment as xEYES BLOOD, TEMP, PLAYER, @@ -395,8 +469,8 @@ const enum { ADD_STRUCT_NODE= 0, //UNUSED MULT_MAXHEALTH, //UNUSED ADD_INT_MAXAGE, //UNUSED - MULT_MUTCHANCE, //old MUTRATE1 - MULT_MUTSIZE, //old MUTRATE2 + MULT_BRAIN_MUTCHANCE, //old MUTRATE1 + MULT_BRAIN_MUTSIZE, //old MUTRATE2 MULT_RADIUS, MULT_RED, MULT_GREEN, @@ -437,8 +511,8 @@ namespace conf { //DEFAULTS: All of what follows are defaults, and if settings.cfg exists, are subsituted with that file's values if VERSION #'s match //SIM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SIM - const int WIDTH = 10000; //width and height of simulation - const int HEIGHT = 8000; + const int WIDTH = 15000; //width and height of simulation + const int HEIGHT = 12000; const int WWIDTH = 1100; //initial window width and height const int WHEIGHT = 700; @@ -448,17 +522,21 @@ namespace conf { const char SFX_CHIRP1[]= "sounds/agents/chirp1.ogg"; const char SFX_CHIRP2[]= "sounds/agents/chirp2.ogg"; const char SFX_SMOOCH1[]= "sounds/agents/smooch1.ogg"; + const char SFX_PURR1[]= "sounds/agents/purr1.ogg"; const char SFX_PLOP1[]= "sounds/agents/plop1.ogg"; const char SFX_DEATH1[]= "sounds/agents/death1.ogg"; const char SFX_STAB1[]= "sounds/agents/stab1.ogg"; + const char SFX_CHOMP1[]= "sounds/agents/chomp1.ogg"; + const char SFX_CHOMP2[]= "sounds/agents/chomp2.ogg"; const char SFX_BEACH1[]= "sounds/environment/beach1.ogg"; const char SFX_UI_RELATIVESELECT[]= "sounds/interaction/selection1.ogg"; + const std::string MUSIC_FOLDER= "sounds/music/"; const std::string MAIN_SONG= "sounds/music/sleep no more - evanjones4.ogg"; const std::string ETHERAL_SONG= "sounds/music/pinecone-ambient-dark - evanjones4.ogg"; - const std::string ENERGY_SONG= "sounds/music/endlessmotion - bensound.ogg"; + const std::string CELEBRATION_SONG= "sounds/music/endlessmotion - bensound.ogg"; const std::string BABY_SONG= "sounds/music/sad-heaven-piano-3 - psovod.ogg"; const std::string ADAPT_SONG= "sounds/music/sunny dream - kjartan-abel-com.ogg"; const std::string PERSIST_SONG= "sounds/music/ambient-03-08-19 - newagesoup.ogg"; @@ -467,8 +545,11 @@ namespace conf { const std::string SHIMMER_SONG= "sounds/music/pinecone-ambient - evanjones4.ogg"; const std::string OVERGROWTH_SONG= "sounds/music/dunes - andrewkn.ogg"; const std::string STALE_SONG= "sounds/music/msfxp6-198-stretched-piano-1 - erokia.ogg"; - const std::string SONGS[11]= {MAIN_SONG,ETHERAL_SONG,ENERGY_SONG,BABY_SONG,ADAPT_SONG,PERSIST_SONG,RHYTHM_SONG, - SLEEPY_SONG,SHIMMER_SONG,OVERGROWTH_SONG,STALE_SONG}; + const std::string INSPIRE_SONG= "sounds/music/cd-yang-001 - kevp888.ogg"; + const int NUM_SONGS= 12; // must increment when adding new songs above, and add ref to list below + + const std::string SONGS[NUM_SONGS]= {MAIN_SONG,ETHERAL_SONG,CELEBRATION_SONG,BABY_SONG,ADAPT_SONG,PERSIST_SONG,RHYTHM_SONG, + SLEEPY_SONG,SHIMMER_SONG,OVERGROWTH_SONG,STALE_SONG,INSPIRE_SONG}; const float SNAP_SPEED = 0.2; //how fast snapping to an object of interest is; 1 is instant, 0.1 is smooth, 0 is pointless const float ZOOM_SPEED = 0.002; //how fast zoom actions change the magnification @@ -480,10 +561,12 @@ namespace conf { const float RENDER_MAXSPLASHSIZE= 35.0; //max indicator splash size on any agent const float RENDER_MINVOLUME= 0.01; //min volume below which the agent is considered silent (for visualizing purposes, not real) - const int REPORTS_PER_EPOCH = 200; //(.cfg) - const int FRAMES_PER_EPOCH = 200000; //(.cfg) - const int FRAMES_PER_DAY= 4000; //(.cfg) - const int RECORD_SIZE = 200; // number of data points stored for the graph. Units are in reports, the frequency of which are defined above + const int REPORTS_PER_EPOCH = 500; //(.cfg) + const int FRAMES_PER_EPOCH = 400000; //(.cfg) + const int FRAMES_PER_DAY= 8000; //(.cfg) + const int CELL_TICK_RATE= 4; //Tick rate of all cells in the world. IMPACTS PERFORMANCE!!! 1= every cell calculated every tick, + // 2= every other tick for every other cell, 4= every 4th cell is calculated every 4 ticks, etc. This value also multiplies all cell + // growth/decay rates so no differences should be observed. Setting to 5+ will cause light to look weird, and has diminishing performance returns //WORLD ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ WORLD const int CZ= 50; //cell size in pixels, for food squares. Should divide well into Width, Height @@ -496,27 +579,32 @@ namespace conf { const int CONTINENTS= 2; //(.cfg) const int CONTINENT_SPREAD= 20; //how many cells each continent "seed" will, at max, spread from another const float LOWER_ELEVATION_CHANCE= 0.08; //what's the chance that the terrain will drop a level instead of stay the same when "spreading"? - const float OCEANPERCENT= 0.6; //(.cfg) + const float OCEANPERCENT= 0.65; //(.cfg) const bool SPAWN_LAKES= true; //(.cfg) const int AGENTS_MIN_NOTCLOSED= 50; //(.cfg) const int AGENTS_MAX_SPAWN= 400; //(.cfg) const int AGENTSPAWN_FREQ= 75; //(.cfg) - const int AGENTS_MAX_NOOXYGEN= 2500; //(.cfg) + const int AGENTS_MAX_NOOXYGEN= 2400; //(.cfg) const int SPECIESID_RANGE= 9000; //species ID range between (-this,this) that agents will spawn with. Note it is not capped - const float GRAVITYACCEL= 0.010; //(.cfg) + const float GRAVITY_ACCELERATION= 0.010; //(.cfg) const float BUMP_PRESSURE= 0.1; //(.cfg) const float GRAB_PRESSURE= 0.1; //(.cfg) - const float SOUNDPITCHRANGE= 0.1; //(.cfg) - const float LIGHT_AMBIENT_PERCENT= 0.25; //multiplier of the natural light level that eyes will always see. Be cautious with this. + const float SOUND_PITCH_RANGE= 0.1; //(.cfg) + const float WHEEL_VOLUME= 0.1; //multiplier of the wheel speeds when being heard + const float WHEEL_TONE= 0.125; //tone value that wheels are heard at, in range [0,1] + const float AMBIENT_LIGHT_PERCENT= 0.25; //(.cfg) + const bool AGENTS_SEE_CELLS = true; //(.cfg & save) + const bool AGENTS_DONT_OVERDONATE = false; //(.cfg & save) const bool DISABLE_LAND_SPAWN= true; //(.cfg & GUI) const bool MOONLIT= true; //(.cfg, save, & GUI) - const float MOONLIGHTMULT= 0.1; //(.cfg & save) + const float MOONLIGHTMULT= 0.25; //(.cfg & save) const bool DROUGHTS= true; //(.cfg, save, & GUI) - const float DROUGHT_STDDEV= 0.1; // The standard deviation of changes to the DROUGHTMULT - const int DROUGHT_WEIGHT= 2; // the weight multiple of the current DROUGHTMULT when averaged (1.0 has a weight of 1) + const float DROUGHT_STDDEV= 0.3; // The standard deviation of changes to the DROUGHTMULT + const int DROUGHT_WEIGHT_TO_RANDOM= 31; //the weight multiple of the current DROUGHTMULT when averaged DOWN (randf(0,1) has a weight of 1) + const int DROUGHT_WEIGHT_TO_ONE= 2; //the weight multiple of the current DROUGHTMULT when averaged to ONE (1.0 has a weight of 1). changing this relative to _ZERO should be interesting const float DROUGHT_NOTIFY= 0.2; //+/- this value from 1.0 of drought shows notifications const float DROUGHT_MIN= 0.6; //(.cfg & save) const float DROUGHT_MAX= 1.5; //(.cfg & save) @@ -524,65 +612,84 @@ namespace conf { const int MUTEVENT_MAX= 3; //(.cfg) const float MUTEVENT_CHANCE= 0.25; //chance of a mutation event that has a multiple other than 1 const bool CLIMATE= true; //(.cfg, save, & GUI) - const float CLIMATE_INTENSITY= 0.007; //(.cfg & GUI) + const float CLIMATE_INTENSITY= 0.009; //(.cfg & GUI) + const float CLIMATE_INTENSITY_EPOCH_MULT= 8.0; //multiplier applied to the above value when selecting new climate values at start of epochs const float CLIMATEMULT_AVERAGE= 0.5; //value that the world's climate mult tries to average back towards + const int CLIMATEMULT_WEIGHT= 3; // the weight multiple of the current CLIMATEMULT when averaged (CLIMATEMULT_AVERAGE has a weight of 1) + const bool CLIMATE_AFFECT_FLORA= true; //(.cfg) + const float CLIMATE_KILL_FLORA_ZONE= 0.1; //temperature zone (in absolute units) that flora struggles within. A value of 0 disables plant decay, so using CLIMATE_AFFECT_FLORA= true only reduces seeding //BOTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOTS //brain settings - const int BRAINSIZE= 160; //(.cfg) + const int BRAINBOXES= 70 + Output::OUTPUT_SIZE; //(.cfg) + const int BRAINCONNS= 300; //(.cfg) const float LEARNRATE= 0.001; // CHANGE TO LEARN FROM USER INPUT const bool ENABLE_LEARNING= true; // - const float BRAIN_DIRECTINPUTS= 0.2; //probability of random brain conns on average which will connect directly to inputs + const float BRAIN_DIRECTINPUTS= 0.4; //probability of random brain conns on average which will connect directly to inputs const float BRAIN_DEADCONNS= 0.35; //probability of random brain conns which are "dead" (that is, weight = 0) const float BRAIN_CHANGECONNS= 0.05; //probablility of random brain conns which are change sensitive - const float BRAIN_MEMCONNS= 0.01; //probablility of random brain conns which are memory type +// const float BRAIN_MEMCONNS= 0.01; //probablility of random brain conns which are memory type + const float BRAIN_MIRRORCONNS= 0.1; //BRAINCONNS*this additional connections will be made as mirror-compare connections with a random other connection + const float BRAIN_CONN_ID_MUTATION_STD_DEV= 1.0; //standard dev. for the brain connection ID change mutation. UNUSED! todo const float BRAIN_TRACESTRENGTH= 0.1; //when performing a traceback, what minimum absolute weight of connections will count for tracing -// const float BRAIN_MIRRORCONNS= 0.05; // -// const float BRAIN_ANDCONNS= 0.2; //probability of random brain conns that multiply in instead of add. //general settings - const float WHEEL_SPEED= 0.3; //(.cfg) + const float WHEEL_SPEED= 0.5; //(.cfg) const float WHEEL_LOCATION= 0.5; //proportion of the agent's radius that the wheels are located const float ENCUMBEREDMULT= 0.3; //speed multiplier for being encumbered - const float MEANRADIUS=10.0; //(.cfg) - const float BOOSTSIZEMULT= 2.0; //(.cfg) - const float BOOSTEXAUSTMULT= 4.0; //(.cfg) - const float BASEEXHAUSTION= -7; //(.cfg) - const float EXHAUSTION_MULT= 0.5; //(.cfg) + const float MEANRADIUS= 10.0; //(.cfg) + const float JUMP_MOVE_BONUS_MULT= 2.0; //(.cfg) + const float BOOST_MOVE_MULT= 2.0; //(.cfg) + const float BOOST_EXAUSTION_MULT= 1.2; //(.cfg) + const float BASEEXHAUSTION= -12; //(.cfg) + const float EXHAUSTION_MULT_PER_OUTPUT= 1.0; //(.cfg) + const float EXHAUSTION_MULT_PER_CONN= 0.02; //(.cfg) const int MAXWASTEFREQ= 200; //(.cfg) - const float FOODTRANSFER= 0.1; //(.cfg) + const float GENEROCITY_RATE= 0.1; //(.cfg) const float MAXSELFISH= 0.01; //Give value below which an agent is considered selfish - const float SPIKESPEED= 0.01; //(.cfg) + const float SPIKESPEED= 0.003; //(.cfg) const float VELOCITYSPIKEMIN= 0.2; //minimum velocity difference between two agents in the positive direction to be spiked by the other + const bool SPAWN_MIRROR_EYES = true; //(.cfg) + const bool PRESERVE_MIRROR_EYES = true; //(.cfg) + const float MIN_TEMP_DISCOMFORT_THRESHOLD = 0.005; //minimum discomfort value below which it's just overridden to 0 + + //visual settings const int BLINKTIME= 8; //it's really a little thing... how many ticks the agent eyes blink for. Purely aesthetic const int BLINKDELAY= 105; //blink delay time. In ticks - const int JAWRENDERTIME= 25; //time allowed for jaw to be rendered after a bite starts + const int JAWRENDERTIME= 20; //time allowed for jaw to be rendered after a bite starts + const int INPUTS_OUTPUTS_PER_ROW = 20; //visually how many inputs and outputs are we showing before starting a new row? + const int BOXES_PER_ROW = 30; //same as above, but for boxes //reproduction const int TENDERAGE= 10; //(.cfg) const float MINMOMHEALTH=0.25; //(.cfg) const float MIN_INTAKE_HEALTH_RATIO= 0.5; //(.cfg) - const float REP_PER_BABY= 4; //(.cfg) - const float REPCOUNTER_MIN= 15; //minimum value the Repcounter may be set to + const float REP_PER_BABY= 3; //(.cfg) + const float REPCOUNTER_MIN= 8; //minimum value the Repcounter may be set to const float OVERHEAL_REPFILL= 0; //(.cfg) //mutations - const float MAXDEVIATION= 10; // no longer used, kept for loading legacy worlds + const int OVERRIDE_KINRANGE= -1; //(.cfg) + const int VISUALIZE_RELATED_RANGE= 30; // what's the range in addition to agent's kinrange that we go ahead and say maybe they were related const int BRAINSEEDHALFTOLERANCE= 5; //the difference in brain seeds before halving. A difference = this between brain seeds corresponds to 25%/75% chances - const float META_MUTCHANCE= 0.2; //what is the chance and stddev of mutations to the mutation chances and sizes? lol - const float META_MUTSIZE= 0.0015; - const float DEFAULT_MUTCHANCE= 0.11; //(.cfg) - const float DEFAULT_MUTSIZE= 0.02; //(.cfg) + const float META_MUTCHANCE= 0.1; //what is the chance and stddev of mutations to the mutation chances and sizes? lol + const float META_MUTSIZE= 0.003; const float LIVE_MUTATE_CHANCE= 0.0001; //(.cfg) + const float DEFAULT_BRAIN_MUTCHANCE= 0.03; //(.cfg) + const float DEFAULT_BRAIN_MUTSIZE= 0.01; //(.cfg) + const float DEFAULT_GENE_MUTCHANCE= 0.05; //(.cfg) + const float DEFAULT_GENE_MUTSIZE= 0.03; //(.cfg) //distances - const float DIST= 400; //(.cfg) - const float SPIKELENGTH=30; //(.cfg) - const float TOOCLOSE=8; //(.cfg) + const float MAX_SENSORY_DISTANCE= 400; //(.cfg) + const float SPIKE_LENGTH=30; //(.cfg) + const float BITE_DISTANCE = 12; //(.cfg) + const float BUMP_DAMAGE_OVERLAP=8; //(.cfg) const float FOOD_SHARING_DISTANCE= 60; //(.cfg) const float SEXTING_DISTANCE= 60; //(.cfg) const float GRABBING_DISTANCE= 40; //(.cfg) + const float SMELL_DIST_MULT= 0.5; //multiplier for cell and agent smell distance // const float BLOOD_SENSE_DISTANCE= 50; //(.cfg) //life and death things @@ -595,29 +702,31 @@ namespace conf { //Health losses const int MAXAGE=10000; //(.cfg) const float HEALTHLOSS_AGING = 0.0001; //(.cfg) + const float HEALTHLOSS_EXHAUSTION = 0.0001; //(.cfg) const float HEALTHLOSS_WHEELS = 0.0; //(.cfg) const float HEALTHLOSS_BOOSTMULT= 2.0; //(.cfg) - const float HEALTHLOSS_BADTEMP = 0.0055; //(.cfg) + const float HEALTHLOSS_BADTEMP = 0.008; //(.cfg) const float HEALTHLOSS_BRAINUSE= 0.0; //(.cfg) const float HEALTHLOSS_SPIKE_EXT= 0.0; //(.cfg) - const float HEALTHLOSS_BADTERRAIN= 0.022; //(.cfg) - const float HEALTHLOSS_NOOXYGEN= 0.0002; //(.cfg) - const float HEALTHLOSS_ASSEX= 0.25; //(.cfg) + const float HEALTHLOSS_BADTERRAIN= 0.021; //(.cfg) + const float HEALTHLOSS_NOOXYGEN= 0.0001; //(.cfg) + const float HEALTHLOSS_ASSEX= 0.15; //(.cfg) const float DAMAGE_FULLSPIKE= 4.0; //(.cfg) - const float DAMAGE_COLLIDE= 3.0; //(.cfg) + const float DAMAGE_COLLIDE= 2.0; //(.cfg) const float DAMAGE_JAWSNAP= 3.0; //(.cfg) //Death cause strings. Typically preceeded by "Killed by ", but this is just all damage sources in text form - //please note: there are two "sections" sepparated by a single space. In reporting, this is used for uniquely identifying death causes + //please note: there are two "words" sepparated by a single space. In reporting, this is used for uniquely identifying death causes //The number of these should match exactly the above sources of health loss and damage, plus generosity transfer and user kill, minus boostmult and wheels (natural) + const char DEATH_TOOYOUNG[]= "Mutation? TooYoung"; const char DEATH_SPIKERAISE[]= "Spike Raising"; const char DEATH_HAZARD[]= "a Hazard"; const char DEATH_BADTERRAIN[]= "Suffocation ."; - const char DEATH_GENEROUSITY[]= "Excessive Generosity"; - const char DEATH_COLLIDE[]= "Excessive Generosity"; - const char DEATH_SPIKE[]= "a Murder"; - const char DEATH_BITE[]= "a Murder"; //these are currently the same, because currently I'm not monitoring bites vs stabs. will change later + const char DEATH_GENEROSITY[]= "Excessive Generosity"; + const char DEATH_COLLIDE[]= "a Collision"; + const char DEATH_SPIKE[]= "a Stab!"; + const char DEATH_BITE[]= "a Bite!"; const char DEATH_NATURAL[]= "Natural Causes"; const char DEATH_TOOMANYAGENTS[]= "LackOf Oxygen"; //again, this is funky because reporting is expecting only one space... const char DEATH_BADTEMP[]= "Temp Discomfort"; @@ -626,40 +735,42 @@ namespace conf { //LAYERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LAYERS - const float STOMACH_EFF= 0.2; //(.cfg) + const float STOMACH_EFFICIENCY= 0.2; //(.cfg) const float CARNIVORE_MEAT_EFF= 0.125; //0.05; //highest meat mult possible from full carnivores. The carivore stomach is sqrt-ed for even harsher punishment - const char FOOD_TEXT[]= "Plant Food"; - const float FOODINTAKE= 0.01; //(.cfg) - const float FOODDECAY = 0.000004; //(.cfg) - const float FOODGROWTH= 0.000003; //(.cfg) - const float FOODWASTE= 0.0023;//0.0007; //(.cfg) - const int FOODADDFREQ= 225; //(.cfg) - const float FOODSPREAD= 0.00012; //(.cfg) - const int FOODRANGE= 2; //(.cfg) + const char PLANT_TEXT[]= "Plant Food"; + const float PLANT_INTAKE= 0.01; //(.cfg) + const float PLANT_DECAY = 0.000004; //(.cfg) + const float PLANT_GROWTH= 0.000003; //(.cfg) + const float PLANT_WASTE= 0.0023;//0.0007; //(.cfg) + const int PLANT_ADD_FREQ= 225; //(.cfg) + const float PLANT_SPREAD= 0.00012; //(.cfg) + const int PLANT_RANGE= 2; //(.cfg) + const float PLANT_TENACITY= 0.1; //(.cfg) //Plant food is the simplest and most plentiful form of nutrition, but it takes time to consume enough const char FRUIT_TEXT[]= "Fruit Food"; - const float FRUITINTAKE = 0.020; //(.cfg) - const float FRUITDECAY = 0.000003; //(.cfg) - const float FRUITWASTE = 0.0023; //0.0014; //(.cfg) - const int FRUITADDFREQ = 4; //(.cfg) - const float FRUITREQUIRE= 0.1; //(.cfg) + const float FRUIT_INTAKE = 0.03; //(.cfg) + const float FRUIT_DECAY = 0.000003; //(.cfg) + const float FRUIT_WASTE = 0.0023; //0.0014; //(.cfg) + const int FRUIT_ADD_FREQ = 2; //(.cfg) + const float FRUIT_PLANT_REQUIRE= 0.1; //(.cfg) //Fruit is a quick and easy alternative to plants. Also partially randomly populated, harkening back to ScriptBots origins const char MEAT_TEXT[]= "Meat Food"; - const float MEATINTAKE= 0.1; //(.cfg) - const float MEATDECAY= 0.00002;//0.00001; //(.cfg) - const float MEATWASTE= 0.008; //0.0023; //(.cfg) - const float MEATVALUE= 1.0; //(.cfg) + const float MEAT_INTAKE= 0.06; //(.cfg) + const float MEAT_DECAY= 0.000005; //(.cfg) + const float MEAT_WASTE= 0.0023; //(.cfg) + const float MEAT_VALUE= 1.0; //(.cfg) + const float MEAT_NON_FRESHKILL_MULT = 0.5; //(.cfg) //Meat comes from dead bots, and is the fastest form of nutrition, IF bots can learn to find it before it decays (or make it themselves...) - const int HAZARDFREQ= 30; //(.cfg) - const float HAZARDEVENT_MULT= 4.0; //(.cfg) - const float HAZARDDECAY= 0.000002; //(.cfg) - const float HAZARDDEPOSIT= 0.00006; //(.cfg) - const float HAZARDDAMAGE= 0.001;//0.0032; //(.cfg) - const float HAZARDPOWER= 0.5; //(.cfg) + const int HAZARD_EVENT_FREQ= 30; //(.cfg) + const float HAZARD_EVENT_MULT= 4.0; //(.cfg) + const float HAZARD_DECAY= 0.000001; //(.cfg) + const float HAZARD_DEPOSIT= 0.00006; //(.cfg) + const float HAZARD_DAMAGE= 0.003;//0.0032; //(.cfg) + const float HAZARD_POWER= 0.5; //(.cfg) } #endif diff --git a/SOURCE/vmath.h b/SOURCE/vmath.h index b572399..8ceed79 100644 --- a/SOURCE/vmath.h +++ b/SOURCE/vmath.h @@ -444,7 +444,7 @@ namespace VMATH_NAMESPACE return (*this) + (r - (*this)) * fact; } - //added functions + //get universal angle (from x-axis) of the vector in the interval [-pi,+pi] radians float get_angle() { if(x==0 && y==0) return 0; diff --git a/sounds/agents/chomp1.ogg b/sounds/agents/chomp1.ogg new file mode 100644 index 0000000..9135116 Binary files /dev/null and b/sounds/agents/chomp1.ogg differ diff --git a/sounds/agents/chomp2.ogg b/sounds/agents/chomp2.ogg new file mode 100644 index 0000000..4de213e Binary files /dev/null and b/sounds/agents/chomp2.ogg differ diff --git a/sounds/agents/purr.ogg b/sounds/agents/purr1.ogg similarity index 100% rename from sounds/agents/purr.ogg rename to sounds/agents/purr1.ogg diff --git a/sounds/interaction/selection2.ogg b/sounds/interaction/selection2.ogg deleted file mode 100644 index d9703d9..0000000 Binary files a/sounds/interaction/selection2.ogg and /dev/null differ diff --git a/sounds/interaction/selection3.ogg b/sounds/interaction/selection3.ogg deleted file mode 100644 index 100f287..0000000 Binary files a/sounds/interaction/selection3.ogg and /dev/null differ diff --git a/sounds/interaction/swoosh1.ogg b/sounds/interaction/swoosh1.ogg deleted file mode 100644 index b791296..0000000 Binary files a/sounds/interaction/swoosh1.ogg and /dev/null differ diff --git a/sounds/music/cd-yang-001 - kevp888.ogg b/sounds/music/cd-yang-001 - kevp888.ogg new file mode 100644 index 0000000..944ee1a Binary files /dev/null and b/sounds/music/cd-yang-001 - kevp888.ogg differ diff --git a/sounds/music/endlessmotion - bensound.ogg b/sounds/music/endlessmotion - bensound.ogg index fc10bb6..5e24aad 100644 Binary files a/sounds/music/endlessmotion - bensound.ogg and b/sounds/music/endlessmotion - bensound.ogg differ