From 686e085edbe8b6984d70f85afca38e5dd90829d8 Mon Sep 17 00:00:00 2001 From: Untrustedlife Date: Fri, 26 Apr 2019 15:35:15 -0500 Subject: [PATCH] all species now descend from the player species processor stuff is now reset in restoreOrganelleLayour fixed bug where species lacked a string code sometimes New epithets (instead of mutated epithets) are more likely to be generated when a species splits, and new genuses are more likely to split off, and changed how species colors work, the more derived a species the more color diference now. Started coming up with a way of coding locations,got rid of unused constants, moved mutation code to procedural_microbes, got rid of case that no longer exists gneralized mutation code so eukaryotes and prokaryotes use the same thing Game now keeps track of species organelle position so mor einteresting shapes can appear and spece sthat split from the player maintain their shape, but in the processi broke mutations commented out debug Prints renamed getPosition method to something more descriptive because ill be using it for that. cleaned up code a bunch, it now uses deleimiters and string.split instead of crazy substring shenanigans cleaned up code to be in line with crodnus suggestion Mutations that delete organelles now work removed useless todo that is now fixed simplified code even more using join, also added an experimental "replacing" mutation Changed replacement code a tad cleaned up species code even more, moved membrane stuff to its own function Rewrote a bunch of procedural_microbes.as adding organelles now works Cleaned up code more, also array gets shuffled when placing new organelles on NPC cells to ensure organelles do not always end up on the same side of the cell, reworded comments to be more accurate Changed AI a bit, to prevent randomly stopping while chasing prey. AI base speed is now equal to cell base speed, there is a bug where AI player cells move unusually fast (about double), and that still exists, but at least the player themselves and the other species AI cells are now on a more equal playing ground. Added some spacing in movement_organelle so its more readable. got rid of pointless normalize Got rid of the cast hhenri mentioned, AI now can choose to move more slowly if they have toxins so they don't ram into cells when trying to shoot toxins at them. --- scripts/microbe_stage/configs.as | 13 +- scripts/microbe_stage/microbe_ai.as | 49 +++- .../microbe_editor/microbe_editor.as | 14 +- scripts/microbe_stage/organelle.as | 2 +- .../movement_organelle.as | 3 + scripts/microbe_stage/procedural_microbes.as | 232 ++++++++++++------ scripts/microbe_stage/species_system.as | 224 ++++++----------- 7 files changed, 290 insertions(+), 247 deletions(-) diff --git a/scripts/microbe_stage/configs.as b/scripts/microbe_stage/configs.as index cdd1761bafe..83e592cf76c 100644 --- a/scripts/microbe_stage/configs.as +++ b/scripts/microbe_stage/configs.as @@ -23,15 +23,17 @@ const auto MAX_SPAWN_DISTANCE = 5000.0f; // Cell Colors const auto MIN_COLOR = 0.0f; const auto MAX_COLOR = 0.9f; + // Too subtle? -const auto MIN_COLOR_MUTATION = -0.005f; -const auto MAX_COLOR_MUTATION = 0.005f; +const auto MIN_COLOR_MUTATION = -0.2f; +const auto MAX_COLOR_MUTATION = 0.2f; const auto MIN_OPACITY = 0.5f; const auto MAX_OPACITY = 1.8f; const auto MIN_OPACITY_CHITIN = 0.4f; const auto MAX_OPACITY_CHITIN = 1.2f; + // Min Opacity Mutation const auto MIN_OPACITY_MUTATION = -0.01f; const auto MAX_OPACITY_MUTATION = 0.01f; @@ -40,9 +42,11 @@ const auto MAX_OPACITY_MUTATION = 0.01f; const auto MUTATION_BACTERIA_TO_EUKARYOTE = 1; const auto MUTATION_CREATION_RATE = 0.1f; const auto MUTATION_DELETION_RATE = 0.1f; +const auto MUTATION_REPLACEMENT_RATE = 0.1f; //Removal cost const auto ORGANELLE_REMOVE_COST = 10; + // Spawn Radius const auto MICROBE_SPAWN_RADIUS = 150; const auto BACTERIA_SPAWN_RADIUS = 150; @@ -101,8 +105,11 @@ const auto REGENERATION_RATE = 1.0f; const auto FLAGELLA_ENERGY_COST = 7.1f; const auto FLAGELLA_BASE_FORCE = 0.7f; const auto CELL_BASE_THRUST = 1.6f; + // is set by this and modified by applyCellMovement like the player later -const auto AI_BASE_MOVEMENT = 1.0f; +const auto AI_BASE_MOVEMENT = 1.6f; +const auto AI_FOCUSED_MOVEMENT = 1.0f; + //! The drag force is calculated by taking the current velocity and multiplying it by this. //! This must be negative! const auto CELL_DRAG_MULTIPLIER = -0.12f; diff --git a/scripts/microbe_stage/microbe_ai.as b/scripts/microbe_stage/microbe_ai.as index 512b00d2b5a..1d088e2be04 100644 --- a/scripts/microbe_stage/microbe_ai.as +++ b/scripts/microbe_stage/microbe_ai.as @@ -51,6 +51,8 @@ class MicrobeAIControllerComponent : ScriptComponent{ ObjectID prey = NULL_OBJECT; ObjectID targetChunk = NULL_OBJECT; ObjectID predator = NULL_OBJECT; + bool moveThisHunt = true; + bool moveFocused = false; bool preyPegged=false; // Prey and predator lists array predatoryMicrobes; @@ -521,12 +523,17 @@ class MicrobeAISystem : ScriptSystem{ aiComponent.hasTargetPosition = true; //Always set target Position, for use later in AI - if (aiComponent.speciesAggression+GetEngine().GetRandom().GetNumber(-100.0f,100.0f) > aiComponent.speciesActivity){ - microbeComponent.movementDirection = Float3(0, 0, -AI_BASE_MOVEMENT); + if (aiComponent.moveThisHunt){ + if (aiComponent.moveFocused){ + microbeComponent.movementDirection = Float3(0.0f,0.0f,-AI_FOCUSED_MOVEMENT); } else{ - microbeComponent.movementDirection = Float3(0, 0, 0); + microbeComponent.movementDirection = Float3(0.0f,0.0f,-AI_BASE_MOVEMENT); } + } + else{ + microbeComponent.movementDirection = Float3(0, 0, 0); + } // Turn off engulf if prey is Dead // This is probabbly not working @@ -614,11 +621,11 @@ class MicrobeAISystem : ScriptSystem{ aiComponent.targetPosition = world.GetComponent_Position(chunk)._Position; auto vec = (aiComponent.targetPosition - position._Position); aiComponent.direction = vec.Normalize(); - microbeComponent.facingTargetPoint = aiComponent.targetPosition; + microbeComponent.facingTargetPoint = aiComponent.targetPosition; aiComponent.hasTargetPosition = true; //Always set target Position, for use later in AI - microbeComponent.movementDirection = Float3(0, 0, -AI_BASE_MOVEMENT); + microbeComponent.movementDirection = Float3(0.0f,0.0f,-AI_BASE_MOVEMENT); // Turn off engulf if chunk is gone if (engulfableComponent is null){ @@ -707,7 +714,7 @@ class MicrobeAISystem : ScriptSystem{ auto vec = (position._Position-aiComponent.targetPosition); aiComponent.direction = vec.Normalize(); microbeComponent.facingTargetPoint = -aiComponent.targetPosition; - microbeComponent.movementDirection = Float3(0, 0, -(AI_BASE_MOVEMENT)); + microbeComponent.movementDirection = Float3(0.0f,0.0f,-AI_BASE_MOVEMENT); aiComponent.hasTargetPosition = true; //Freak out and fire toxins everywhere @@ -727,17 +734,17 @@ class MicrobeAISystem : ScriptSystem{ //LOG_INFO("evaluating"); MicrobeAIControllerComponent@ aiComponent = components.first; Position@ position = components.third; + MicrobeComponent@ microbeComponent = components.second; + CompoundId oxytoxyId = SimulationParameters::compoundRegistry().getTypeId("oxytoxy"); + int numberOfAgentVacuoles = int( + microbeComponent.specialStorageOrganelles[formatUInt(oxytoxyId)]); + //rollCheck(aiComponent.speciesOpportunism,500.0f) if (rollCheck(aiComponent.speciesOpportunism,500.0f)) { aiComponent.lifeState = SCAVENGING_STATE; aiComponent.boredom = 0; } - else if (rollCheck(aiComponent.speciesActivity,500.0f)) - { - aiComponent.lifeState = PLANTLIKE_STATE; - aiComponent.boredom = 0; - } else { if (prey != NULL_OBJECT && predator != NULL_OBJECT) { @@ -745,6 +752,12 @@ class MicrobeAISystem : ScriptSystem{ if (GetEngine().GetRandom().GetNumber(0.0f,aiComponent.speciesAggression) > GetEngine().GetRandom().GetNumber(0.0f,aiComponent.speciesFear) && (aiComponent.preyMicrobes.length() > 0)){ + aiComponent.moveThisHunt=!rollCheck(aiComponent.speciesActivity,500.0f); + + if (numberOfAgentVacuoles > 0){ + aiComponent.moveFocused = rollCheck(aiComponent.speciesFocus,500.0f); + } + aiComponent.lifeState = PREDATING_STATE; } else if (GetEngine().GetRandom().GetNumber(0.0f,aiComponent.speciesAggression) < @@ -756,6 +769,12 @@ class MicrobeAISystem : ScriptSystem{ else if (aiComponent.speciesAggression == aiComponent.speciesFear && (aiComponent.preyMicrobes.length() > 0)){ // Prefer predating (makes game more fun) + aiComponent.moveThisHunt=!rollCheck(aiComponent.speciesActivity,500.0f); + + if (numberOfAgentVacuoles > 0){ + aiComponent.moveFocused = rollCheck(aiComponent.speciesFocus,500.0f); + } + aiComponent.lifeState = PREDATING_STATE; } else if (rollCheck(aiComponent.speciesFocus,500.0f) && GetEngine().GetRandom().GetNumber(0,10) <= 2){ @@ -764,6 +783,12 @@ class MicrobeAISystem : ScriptSystem{ } else if (prey != NULL_OBJECT){ //LOG_INFO("prey only"); + aiComponent.moveThisHunt=!rollCheck(aiComponent.speciesActivity,500.0f); + + if (numberOfAgentVacuoles > 0){ + aiComponent.moveFocused = rollCheck(aiComponent.speciesFocus,500.0f); + } + aiComponent.lifeState = PREDATING_STATE; } else if (predator != NULL_OBJECT){ @@ -845,7 +870,7 @@ class MicrobeAISystem : ScriptSystem{ auto vec = (aiComponent.targetPosition - position._Position); aiComponent.direction = vec.Normalize(); microbeComponent.facingTargetPoint = aiComponent.targetPosition; - microbeComponent.movementDirection = Float3(0, 0, -AI_BASE_MOVEMENT); + microbeComponent.movementDirection = Float3(0.0f,0.0f,-AI_BASE_MOVEMENT); aiComponent.hasTargetPosition = true; } diff --git a/scripts/microbe_stage/microbe_editor/microbe_editor.as b/scripts/microbe_stage/microbe_editor/microbe_editor.as index c77f922fa56..64573f7dbf7 100644 --- a/scripts/microbe_stage/microbe_editor/microbe_editor.as +++ b/scripts/microbe_stage/microbe_editor/microbe_editor.as @@ -125,10 +125,20 @@ class MicrobeEditor{ playerSpecies.organelles); editedMicrobe.resize(0); + playerSpecies.stringCode=""; for(uint i = 0; i < templateOrganelles.length(); ++i){ - editedMicrobe.insertLast(cast(templateOrganelles[i])); + auto organelle = cast(templateOrganelles[i]); + editedMicrobe.insertLast(organelle); + playerSpecies.stringCode+=organelle._organelle.gene; + // This will always be added after each organelle so its safe to assume its there + playerSpecies.stringCode+=","+organelle.q+","+ + organelle.r+","+ + organelle.rotation; + if (i != templateOrganelles.length()-1){ + playerSpecies.stringCode+="|"; + } } - + LOG_INFO(playerSpecies.stringCode); LOG_INFO("Starting microbe editor with: " + editedMicrobe.length() + " organelles in the microbe"); diff --git a/scripts/microbe_stage/organelle.as b/scripts/microbe_stage/organelle.as index 76c49fdde5d..f7d18abcf39 100644 --- a/scripts/microbe_stage/organelle.as +++ b/scripts/microbe_stage/organelle.as @@ -873,7 +873,7 @@ class PlacedOrganelle : SpeciesStoredOrganelleType{ } } - private Organelle@ _organelle; + Organelle@ _organelle; // q and r are radial coordinates instead of cartesian // Could use the class AxialCoordinates here diff --git a/scripts/microbe_stage/organelle_components/movement_organelle.as b/scripts/microbe_stage/organelle_components/movement_organelle.as index aaeb013803d..45b58dfe6a1 100644 --- a/scripts/microbe_stage/organelle_components/movement_organelle.as +++ b/scripts/microbe_stage/organelle_components/movement_organelle.as @@ -205,9 +205,12 @@ class MovementOrganelle : OrganelleComponent{ MicrobeComponent@ microbeComponent = cast( organelle.world.GetScriptComponentHolder("MicrobeComponent").Find(microbeEntity)); auto pos = organelle.world.GetComponent_Position(microbeEntity); + + const auto force = calculateMovementForce(microbeEntity, organelle, logicTime, microbeComponent, pos); + if(force != Float3(0, 0, 0)) microbeComponent.addMovementForce(force); } diff --git a/scripts/microbe_stage/procedural_microbes.as b/scripts/microbe_stage/procedural_microbes.as index 0b1206d4cc0..48f8404bdf8 100644 --- a/scripts/microbe_stage/procedural_microbes.as +++ b/scripts/microbe_stage/procedural_microbes.as @@ -1,17 +1,7 @@ #include "configs.as" #include "nucleus_organelle.as" #include "hex.as" - -// Limits the size of the initial stringCodes -const auto MIN_INITIAL_LENGTH = 2; -const auto MAX_INITIAL_LENGTH = 15; - -const auto MIN_INITIAL_EPIC_LENGTH = 30; -const auto MAX_INITIAL_EPIC_LENGTH = 70; - -const auto MIN_INITIAL_EPIC_BACTERIA_LENGTH = 3; -const auto MAX_INITIAL_EPIC_BACTERIA_LENGTH = 30; - +// Lists of valid organelles to choose from for mutation dictionary organelleLetters = {}; array VALID_ORGANELLES = {}; array VALID_ORGANELLE_LETTERS = {}; @@ -55,6 +45,8 @@ void setupOrganelleLetters(){ // TODO: verify that this has a good chance of returning also the last organelle // TODO: is there a way to make this run faster? string getRandomLetter(bool isBacteria){ + + // This is actually essentially the entire mutation system here if (!isBacteria) { float i = GetEngine().GetRandom().GetNumber(0.f, maxEukaryoteScore); @@ -86,18 +78,19 @@ string getRandomLetter(bool isBacteria){ // Checks whether an organelle in a certain position would fit within a list of other organelles. bool isValidPlacement(const string &in organelleName, int q, int r, int rotation, - const array &in organelleList + array@ organelleList ) { - // This is super hacky :/ - // this is now slightly less hacky + + // this is now slightly less hacky but it could be btter auto organelleHexes = getOrganelleDefinition(organelleName).getRotatedHexes(rotation); for(uint i = 0; i < organelleList.length(); ++i){ auto otherOrganelle = organelleList[i]; + auto organelleDef = getOrganelleDefinition(otherOrganelle.organelle.name); - auto otherOrganelleHexes = getOrganelleDefinition(otherOrganelle.type).getRotatedHexes( - otherOrganelle.rotation); + // The organelles hexes + auto otherOrganelleHexes = organelleDef.getRotatedHexes(organelleList[i].rotation); for(uint thisHexIndex = 0; thisHexIndex < organelleHexes.length(); ++thisHexIndex){ @@ -119,101 +112,186 @@ bool isValidPlacement(const string &in organelleName, int q, int r, int rotation } // Finds a valid position to place the organelle and returns it -// Maybe the values should be saved? -OrganelleTemplatePlaced@ getPosition(const string &in organelleName, - const array &in organelleList +// We should be able to get far more creative with our cells now +OrganelleTemplatePlaced@ getRealisticPosition(const string &in organelleName, + array@ organelleList ) { int q = 0; int r = 0; - // Checks whether the center is free. - for(int j = 0; j <= 5; ++j){ - int rotation = 360 * j / 6; - if(isValidPlacement(organelleName, q, r, rotation, organelleList)){ - return OrganelleTemplatePlaced(organelleName, q, r, rotation); - } - } + auto organelleShuffledArray = organelleList; - // Moving the center one hex to the bottom. - // This way organelles are "encouraged" to be on the bottom, rather than on the top, - // which in turn means the flagellum are more likely to be on the back side of the cell. - auto initialOffset = Int2(HEX_NEIGHBOUR_OFFSET[formatInt(int(HEX_SIDE::TOP))]); - q = q + initialOffset.X; - r = r + initialOffset.Y; - - // Spiral search for space for the organelle - int radius = 1; - - while(true){ - //Moves into the ring of radius "radius" and center the old organelle - Int2 radiusOffset = Int2(HEX_NEIGHBOUR_OFFSET[ - formatInt(int(HEX_SIDE::BOTTOM_LEFT))]); - q = q + radiusOffset.X; - r = r + radiusOffset.Y; - - //Iterates in the ring - for(int side = 1; side <= 6; ++side){ - Int2 offset = Int2(HEX_NEIGHBOUR_OFFSET[formatInt(side)]); - //Moves "radius" times into each direction - for(int i = 1; i <= radius; ++i){ + // Shuffle the Array to make sure its not always placing at the same part of the cell + organelleShuffledArray.sort( function(a,b) { + return GetEngine().GetRandom().GetNumber(0,100) <= 50; + } + ); + + // Loop through all the organelles and find an open spot to place our new organelle attached to existing organelles + // This almost always is over at the first iteration, so its not a huge performance hog + for(uint i = 0; i < organelleShuffledArray.length(); ++i){ + // The organelle we wish to be next to + auto otherOrganelle = organelleShuffledArray[i].organelle; + auto organelleDef = getOrganelleDefinition(otherOrganelle.name); + + // The organelles hexes + auto hexes = organelleDef.getRotatedHexes(organelleShuffledArray[i].rotation); + + // Middle of our organelle + q = organelleShuffledArray[i].q; + r = organelleShuffledArray[i].r; + + for(uint z = 0; z < hexes.length(); ++z){ + // Off set by hexes in organelle we are looking at + q+=hexes[z].q; + r+=hexes[z].r; + + for(int side = 1; side <= 6; ++side){ + Int2 offset = Int2(HEX_NEIGHBOUR_OFFSET[formatInt(side)]); + // Offset by hex offset q = q + offset.X; r = r + offset.Y; - //Checks every possible rotation value. + //Check every possible rotation value. for(int j = 0; j <= 5; ++j){ - int rotation = (360 * j / 6); - - if(isValidPlacement(organelleName, q, r, rotation, organelleList)){ + if(isValidPlacement(organelleName, q, r, rotation, organelleShuffledArray)){ return OrganelleTemplatePlaced(organelleName, q, r, rotation); } } } - } - ++radius; - } + //Gotta reset each time + q = organelleShuffledArray[i].q; + r = organelleShuffledArray[i].r; + } + } + // We didnt find an open spot, that doesnt mak emuch sense return null; } +// This function takes in a positioning block from the string code and a name +// and returns an organelle with the correct position info +OrganelleTemplatePlaced@ getStringCodePosition(const string &in organelleName, string code){ + //LOG_INFO(code); + + array@ chromArray = code.split(","); + //TODO:Need to add some proper error handling + int q = 0; + int r = 0; + int rotation = 0; + + q=parseInt(chromArray[1]); + + //LOG_INFO(""+q); + r=parseInt(chromArray[2]); + + //LOG_INFO(""+r); + + rotation=parseInt(chromArray[3]); + + //LOG_INFO(""+rotation); + + return OrganelleTemplatePlaced(organelleName, q, r, rotation); +} + // Creates a list of organelles from the stringCode. array@ positionOrganelles(const string &in stringCode){ // TODO: remove once this works - LOG_INFO("DEBUG: positionOrganelles stringCode: " + stringCode); + //LOG_INFO("DEBUG: positionOrganelles stringCode: " + stringCode); array@ result = array(); - array organelleList; + array@ chromArray = stringCode.split("|"); + for(uint i = 0; i < chromArray.length(); ++i){ + OrganelleTemplatePlaced@ pos; + string geneCode = chromArray[i]; + + if (geneCode.length() > 0){ + const auto letter = CharacterToString(geneCode[0]); + //LOG_WRITE(formatUInt(i) + ": " + letter); + string name = string(organelleLetters[letter]); + @pos = getStringCodePosition(name,geneCode); + + if(pos.type == ""){ + assert(false, "positionOrganelles: organelleLetters didn't have the " + "current letter: " + letter); + } - for(uint i = 0; i < stringCode.length(); ++i){ + result.insertLast(PlacedOrganelle(getOrganelleDefinition(pos.type), pos.q, pos.r, + pos.rotation)); + } + } - OrganelleTemplatePlaced@ pos; - const auto letter = CharacterToString(stringCode[i]); - // LOG_WRITE(formatUInt(i) + ": " + letter); - string name = string(organelleLetters[letter]); - //this places the nucleus - if(i == 0){ + return result; +} - @pos = OrganelleTemplatePlaced(name, 0, 0, 360); +//! Mutates a species' dna code randomly - } else { - @pos = getPosition(name, organelleList); - } +string translateOrganelleToGene(OrganelleTemplatePlaced@ ourOrganelle){ + string completeString = ""; + auto organelle = getOrganelleDefinition(ourOrganelle.type); + completeString=organelle.gene+","+ + ourOrganelle.q+","+ + ourOrganelle.r+","+ + ourOrganelle.rotation; + return completeString; - if(pos.type == ""){ +} + +// Pass in the string code, isbacteria +string mutateMicrobe(const string &in stringCode, bool isBacteria) +{ + array@ chromArray = stringCode.split("|"); + array@ modifiedArray = chromArray; + LOG_INFO(chromArray[0]); + string completeString = ""; + + // Delete or replace an organelle randomly + for(uint i = 0; i < chromArray.length(); i++){ + string chromosomes = chromArray[i]; + // Removing last organelle would be silly + if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_DELETION_RATE && chromosomes.length() > 0){ + if (i != chromArray.length()-1 && CharacterToString(chromosomes[0]) != "N"){ + //LOG_INFO("deleteing"); + //LOG_INFO("chromosomes:"+chromArray[i]); + // Delete organelle and its position + modifiedArray.removeAt(i); - assert(false, "positionOrganelles: organelleLetters didn't have the " - "current letter: " + letter); + } + }else if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_REPLACEMENT_RATE && chromosomes.length() > 0){ + if (CharacterToString(chromosomes[0]) != "N"){ + //LOG_INFO("Replacing"); + //LOG_INFO("chromosomes:"+chromArray[i]); + chromosomes[0]=getRandomLetter(isBacteria)[0]; + if (i != chromArray.length()-1){ + modifiedArray.removeAt(i); + modifiedArray.insertAt(i,chromosomes); + } + else{ + modifiedArray.removeAt(i); + modifiedArray.insertLast(chromosomes); + } + } } - organelleList.insertLast(pos); - result.insertLast(PlacedOrganelle(getOrganelleDefinition(pos.type), pos.q, pos.r, - pos.rotation)); } - // Make sure all were added - assert(stringCode.length() == result.length()); - return result; + completeString = join(modifiedArray,"|"); + + // We can insert new organelles at the end of the list + if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_CREATION_RATE){ + auto organelleList = positionOrganelles(completeString); + const auto letter = getRandomLetter(isBacteria); + string name = string(organelleLetters[letter]); + string returnedGenome = translateOrganelleToGene(getRealisticPosition(name,organelleList)); + //LOG_INFO("Adding"); + //LOG_INFO("chromosomes:"+returnedGenome); + completeString+="|"+returnedGenome; + } + + LOG_INFO("Mutated: "+completeString); + return completeString; } diff --git a/scripts/microbe_stage/species_system.as b/scripts/microbe_stage/species_system.as index ce4cd544d04..05c6ae09368 100644 --- a/scripts/microbe_stage/species_system.as +++ b/scripts/microbe_stage/species_system.as @@ -266,19 +266,20 @@ class Species{ Species(SpeciesComponent@ parent, CellStageWorld@ world, bool isBacteria) { this.isBacteria = parent.isBacteria; - + LOG_INFO("Gene Code Is:"+parent.stringCode); if (!isBacteria) { name = randomSpeciesName(); + //Mutate the epithet - if (GetEngine().GetRandom().GetNumber(0, 10) < 8){ + if (GetEngine().GetRandom().GetNumber(0, 10) < 5){ epithet = mutateWord(parent.epithet); } else { epithet = generateNameSection(); } genus = parent.genus; - + colour=parent.colour; mutateBehavior(parent); // Make sure not over or under our scales @@ -286,20 +287,19 @@ class Species{ // Subtly mutate color - if (GetEngine().GetRandom().GetNumber(0,5) == 0) - { - this.colour = Float4(parent.colour.X + randomMutationColourChannel(), - parent.colour.Y + randomMutationColourChannel(), - parent.colour.Z + randomMutationColourChannel(), - parent.colour.W + randomMutationColourChannel()); - } + this.colour = Float4(parent.colour.X + randomMutationColourChannel(), + parent.colour.Y + randomMutationColourChannel(), + parent.colour.Z + randomMutationColourChannel(), + parent.colour.W + randomMutationColourChannel()); + LOG_INFO("X:"+parent.colour.X+" Y:"+parent.colour.Y+" Z:"+parent.colour.Z+" W:"+parent.colour.W); + LOG_INFO("X:"+colour.X+" Y:"+colour.Y+" Z:"+colour.Z+" W:"+colour.W); // Chance of new color needs to be low - if (GetEngine().GetRandom().GetNumber(0,100)==1) + if (GetEngine().GetRandom().GetNumber(0,100) <= 20) { LOG_INFO("New Genus"); // We can do more fun stuff here later - if (GetEngine().GetRandom().GetNumber(0, 10) < 8){ + if (GetEngine().GetRandom().GetNumber(0, 10) < 5){ genus = mutateWord(parent.genus); } else { @@ -312,28 +312,10 @@ class Species{ parent.colour.W + randomMutationColourChannel()); } - this.stringCode = Species::mutate(parent.stringCode); + this.stringCode = mutateMicrobe(parent.stringCode,false); - if (GetEngine().GetRandom().GetNumber(0,100)<=20){ - if (GetEngine().GetRandom().GetNumber(0,100) < 50){ - this.speciesMembraneType = MEMBRANE_TYPE::MEMBRANE; - } - else if (GetEngine().GetRandom().GetNumber(0,100) < 50) { - this.speciesMembraneType = MEMBRANE_TYPE::DOUBLEMEMBRANE; - this.colour.W = randomOpacityChitin();; - } - else if (GetEngine().GetRandom().GetNumber(0,100) < 50) { - this.speciesMembraneType = MEMBRANE_TYPE::WALL; - } - else { - this.speciesMembraneType = MEMBRANE_TYPE::CHITIN; - this.colour.W = randomOpacityChitin(); - } - } - else { - this.speciesMembraneType = parent.speciesMembraneType; - } + generateMembranes(parent); commonConstructor(world); @@ -347,6 +329,28 @@ class Species{ } } + private void generateMembranes(SpeciesComponent@ parent){ + if (GetEngine().GetRandom().GetNumber(0,100)<=20){ + if (GetEngine().GetRandom().GetNumber(0,100) < 50){ + this.speciesMembraneType = MEMBRANE_TYPE::MEMBRANE; + } + else if (GetEngine().GetRandom().GetNumber(0,100) < 50) { + this.speciesMembraneType = MEMBRANE_TYPE::DOUBLEMEMBRANE; + this.colour.W = randomOpacityChitin(); + } + else if (GetEngine().GetRandom().GetNumber(0,100) < 50) { + this.speciesMembraneType = MEMBRANE_TYPE::WALL; + } + else { + this.speciesMembraneType = MEMBRANE_TYPE::CHITIN; + this.colour.W = randomOpacityChitin(); + } + } + else{ + this.speciesMembraneType = parent.speciesMembraneType; + } + } + private void cleanPersonality() { this.aggression = clamp(this.aggression, 0.0f, MAX_SPECIES_AGRESSION); this.fear = clamp(this.fear, 0.0f, MAX_SPECIES_FEAR); @@ -373,7 +377,9 @@ class Species{ { @forWorld = world; + // This translates the genetic code into positions auto organelles = positionOrganelles(stringCode); + // If you have iron (f is the symbol for rusticyanin) if (stringCode.findFirst('f') >= 0) { @@ -585,8 +591,9 @@ class Species{ { name = randomBacteriaName(); genus = parent.genus; + colour=parent.colour; //Mutate the epithet - if (GetEngine().GetRandom().GetNumber(0, 10) < 8){ + if (GetEngine().GetRandom().GetNumber(0, 10) < 5){ epithet = mutateWord(parent.epithet); } else { @@ -600,21 +607,22 @@ class Species{ cleanPersonality(); // Subtly mutate color - if (GetEngine().GetRandom().GetNumber(0, 5)==0) - { - this.colour = Float4(parent.colour.X + randomMutationColourChannel(), - parent.colour.Y + randomMutationColourChannel(), - parent.colour.Z + randomMutationColourChannel(), - parent.colour.W + randomMutationColourChannel()); - } + this.colour = Float4(parent.colour.X + randomMutationColourChannel(), + parent.colour.Y + randomMutationColourChannel(), + parent.colour.Z + randomMutationColourChannel(), + parent.colour.W + randomMutationColourChannel()); - if (GetEngine().GetRandom().GetNumber(0, 100)==1) + + LOG_INFO("X:"+parent.colour.X+" Y:"+parent.colour.Y+" Z:"+parent.colour.Z+" W:"+parent.colour.W); + LOG_INFO("X:"+colour.X+" Y:"+colour.Y+" Z:"+colour.Z+" W:"+colour.W); + + if (GetEngine().GetRandom().GetNumber(0,100) <= 20) { LOG_INFO("New Genus of bacteria"); // We can do more fun stuff here later //Mutate the genus - if (GetEngine().GetRandom().GetNumber(0, 10) < 8){ + if (GetEngine().GetRandom().GetNumber(0, 10) < 5){ genus = mutateWord(parent.genus); } else { @@ -628,27 +636,9 @@ class Species{ parent.colour.W + randomMutationColourChannel()); } - this.stringCode = Species::mutateProkaryote(parent.stringCode); + this.stringCode = mutateMicrobe(parent.stringCode,true); - if (GetEngine().GetRandom().GetNumber(0,100)<=20){ - if (GetEngine().GetRandom().GetNumber(0,100) < 50){ - this.speciesMembraneType = MEMBRANE_TYPE::MEMBRANE; - } - else if (GetEngine().GetRandom().GetNumber(0,100) < 50) { - this.speciesMembraneType = MEMBRANE_TYPE::DOUBLEMEMBRANE; - this.colour.W = randomOpacityChitin();; - } - else if (GetEngine().GetRandom().GetNumber(0,100) < 50) { - this.speciesMembraneType = MEMBRANE_TYPE::WALL; - } - else { - this.speciesMembraneType = MEMBRANE_TYPE::CHITIN; - this.colour.W = randomOpacityChitin(); - } - } - else { - this.speciesMembraneType = parent.speciesMembraneType; - } + generateMembranes(parent); commonConstructor(world); this.setupSpawn(world); @@ -1064,6 +1054,7 @@ void applyTemplate(CellStageWorld@ world, ObjectID microbe, SpeciesComponent@ sp InitialCompound amount = InitialCompound(species.avgCompoundAmounts[ids[i]]); MicrobeOperations::setCompound(world, microbe, compoundId, amount.amount); } + } void restoreOrganelleLayout(CellStageWorld@ world, ObjectID microbeEntity, @@ -1097,6 +1088,11 @@ void restoreOrganelleLayout(CellStageWorld@ world, ObjectID microbeEntity, // Cache isBacteria from species. This can be changed depending on // the added organelles in the editor microbeComponent.isBacteria = species.isBacteria; + + // Call this to reset processor component + Species::initProcessorComponent(world, microbeEntity, species); + // This makes sure that the microbes processer are up to date with their species + Species::copyProcessesFromSpecies(world, species, microbeEntity); } void initProcessorComponent(CellStageWorld@ world, ObjectID entity, @@ -1237,11 +1233,20 @@ ObjectID createSpecies(CellStageWorld@ world, const string &in name, const strin @speciesComponent.avgCompoundAmounts = dictionary(); @speciesComponent.organelles = array(); - for(uint i = 0; i < organelles.length(); i++){ - - // This conversion does a little bit of extra calculations (that are in the - // end not used) - speciesComponent.organelles.insertLast(PlacedOrganelle(organelles[i])); + speciesComponent.stringCode=""; + + // Translate positions over + for(uint i = 0; i < organelles.length(); ++i){ + auto organelle = cast(organelles[i]); + speciesComponent.organelles.insertLast(organelle); + speciesComponent.stringCode+=organelle._organelle.gene; + // This will always be added after each organelle so its safe to assume its there + speciesComponent.stringCode+=","+organelle.q+","+ + organelle.r+","+ + organelle.rotation; + if (i != organelles.length()-1){ + speciesComponent.stringCode+="|"; + } } // Verify it // @@ -1297,91 +1302,6 @@ ObjectID createSpecies(CellStageWorld@ world, const string &in name, const strin return speciesEntity; } - -//! Mutates a species' dna code randomly -string mutate(const string &in stringCode) -{ - // Moving the stringCode to a table to facilitate changes - string chromosomes = stringCode.substr(2); - - // Try to insert a letter at the end of the table. - if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_CREATION_RATE){ - chromosomes += getRandomLetter(false); - } - - // Modifies the rest of the table. - for(uint i = 0; i < stringCode.length(); i++){ - // Index we are adding or erasing chromosomes at - uint index = stringCode.length() - i - 1; - - if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_DELETION_RATE){ - // Removing the last organelle is pointless, that would - // kill the creature (also caused errors). - if (index != stringCode.length()-1) - { - chromosomes.erase(index, 1); - } - } - - if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_CREATION_RATE){ - // There is an error here when we try to insert at the end - // of the list so use insertlast instead in that case - if (index != stringCode.length()-1) - { - chromosomes.insert(index, getRandomLetter(false)); - } - else{ - chromosomes+=getRandomLetter(false); - } - } - } - - // Transforming the table back into a string - // TODO: remove Hardcoded microbe genes - auto newString = "NY" + chromosomes; - return newString; -} - -// Mutate a Bacterium -string mutateProkaryote(const string &in stringCode) -{ - // Moving the stringCode to a table to facilitate changes - string chromosomes = stringCode; - - // Try to insert a letter at the end of the table. - if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_CREATION_RATE){ - chromosomes += getRandomLetter(true); - } - - // Modifies the rest of the table. - for(uint i = 0; i < stringCode.length(); i++){ - // Index we are adding or erasing chromosomes at - uint index = stringCode.length() - i -1; - // Bacteria can be size 1 so removing their only organelle is a bad idea - if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_DELETION_RATE){ - if (index != stringCode.length()-1) - { - chromosomes.erase(index, 1); - } - } - - if(GetEngine().GetRandom().GetNumber(0.f, 1.f) < MUTATION_CREATION_RATE){ - // There is an error here when we try to insert at the end - // of the list so use insertlast instead in that case - if (index != stringCode.length()-1) - { - chromosomes.insert(index, getRandomLetter(true)); - } - else{ - chromosomes+=getRandomLetter(true); - } - } - } - - auto newString = "" + chromosomes; - return newString; -} - //! Calls resetAutoEvo on world's SpeciesSystem void resetAutoEvo(CellStageWorld@ world){ cast(world.GetScriptSystem("SpeciesSystem")).resetAutoEvo();