/// <summary> We've been sent a Stimulus. /// Handle it if possible, or hand it on to our physiology for handling</summary> /// <param name="stimulus">The stimulus information</param> /// <returns>Return true if the stimulus was handled</returns> public bool ReceiveStimulus(Stimulus stim) { // Stimuli that should be handled by ALL cells in a receiving organism go here... switch (stim.Type) { // TODO: Add universal stimulus cases here, then return true case "dummy": return true; } // If that doesn't work, test to see if this cell is actually in range of the sender, // and attempt to handle such stimuli (e.g. being stung or eaten) if (AbsSphere.IsPenetrating(new Sphere(stim.TransmitterLocn, stim.TransmissionRange))==false) return false; // Stimuli that should be handled by cells directly affected go here... switch (stim.Type) { // TODO: Add stimulus cases here, then return true case "dummy": return true; } // If that doesn't work, pass the stimulus on to our Physiology to see if our cell type // can handle it. This allows the stimulus types to be extended return (physiology.ReceiveStimulus(stim)); }
/// <summary> We've been sent a Stimulus. /// <param name="stimulus">The stimulus information</param> /// <returns>Return true if the stimulus was handled</returns> public bool ReceiveStimulus(Stimulus stim) { switch (stim.Type) { // Stimuli that we know how to handle... // TODO: Add stimulus cases here, and return true case "dummy": break; } return false; }
/// <summary> We've been sent a Stimulus. /// Handle it if possible, or hand it on to one of our cells for handling</summary> /// <param name="stimulus">The stimulus information</param> /// <returns>Return true if the stimulus was handled by one or more cells</returns> public bool ReceiveStimulus(Stimulus stim) { bool handled = false; switch (stim.Type) { // Stimuli that we know how to handle... // TODO: Add organism-wide stimulus cases here, and set handled=true // Those we don't know how to handle we send on to all our cells // (the cells then check whether the stimulus was intended for them, by // seeing if their spheres intersect the transmission range) default: foreach (Cell c in partList) { if (c.ReceiveStimulus(stim)==true) handled = true; } break; } return handled; }
/// <summary> /// Given a stimulus, find out how that object is positioned relative to a given hotspot on this cell. /// Cell types use this to establish how much notice they should take of an incoming stimulus (passive sensing). /// Call methods on the returned SensorItem to calculate the angle and/or distance as required. /// </summary> /// <param name="spot">Which hotspot to use</param> /// <param name="range">Current range of hotspot</param> /// <param name="stim">the stimulus we received</param> public SensorItem TestStimulusVisibility(int spot, float range, Stimulus stim) { // Get a matrix that will convert target location into hotspot-relative coordinates // Create a matrix that will rotate objects into a sensor-relative frame Matrix transform = hotspot[spot].CombinedMatrix; transform.Invert(); // transform the source position into sensor-relative coordinates Vector3 relpos = Vector3.TransformCoordinate(stim.TransmitterLocn, transform); // Return a SensorItem with this info (the SensorItem.Object refers to the sender) return new SensorItem(stim.From, relpos); }
/// <summary> /// Common stimulus: Emit a transient sound. (Call repeatedly for continuous noise) /// Any cell can call this - we don't need an effector hotspot. /// Recipients ("ear" celltypes) should combine .TransmissionRange with distance from source to compute perceived loudness /// First parameter of Stimulus is the pitch /// NOTE: CameraShips' root cell types will receive the stimuli and can use the parameters to emit a real (DirectSound) sound to the user /// </summary> /// <param name="range">Distance sound travels</param> /// <param name="pitch">Frequency of sound, for pitch-sensitive or pitch-selective sensors: 0 = low, 1.0 = high</param> /// <param name="timbre">Optional string for the DirectSound file that should be played if this sound is picked up by the current CameraShip</param> protected void Sound(float range, float pitch, string timbre) { // Create the stimulus Stimulus stimulus = new Stimulus((IDetectable)owner, ((IDetectable)owner).ReqLocation(), range, "sound", pitch, timbre, null, null); // Send it to each organism in range SensorItem[] recipients = owner.GetObjectsInRange(-1, range, true, false, false); foreach (SensorItem recipient in recipients) { recipient.Object.ReceiveStimulus(stimulus); } }
/// <summary> We've been sent a Stimulus that our basic Cell object doesn't understand. /// This overload responds to the "bioluminescence" stimulus /// Parameter 0 will be a ColorValue containing the bioluminescent cell's current anim colour /// <param name="stimulus">The stimulus information</param> /// <returns>Return true if the stimulus was handled</returns> public override bool ReceiveStimulus(Stimulus stim) { float direction = 0.5f; float intensity = 0f; if (stim.Type == "bioluminescence") { // Find out if the sender is within range of our hotspots SensorItem cone0 = owner.TestStimulusVisibility(0, range, stim); float dist = cone0.Distance(); // dist will be roughly the same from both hotspots if (dist < range) // if object is within range... { // Find out how visible it is from each hotspot SensorItem cone1 = owner.TestStimulusVisibility(1, range, stim); // now we know we're in range, get the other hotspot's visibility float angle0 = cone0.Angle(); // Get angle from each hotspot float angle1 = cone1.Angle(); if ((angle0 < halfAngle)||(angle1 < halfAngle)) // if within sight of at least one hotspot... { // Compare the light to our filter colour ColorValue light = (ColorValue)stim.Param0; // Stimulus param 0 will be the bioluminescent cell's current anim colour float r1 = light.Red - r; // difference in RGB between filter and cell float g1 = light.Green - g; float b1 = light.Blue - b; float match = 1f - (float)Math.Sqrt((r1 * r1 + g1 * g1 + b1 * b1) / 3f); // least squares measure of similarity (1=identical) // calc intensity and direction, if we match spectrally if (match > 0.8f) // <=1/2 is a bad match, e.g. rgB doesn't match rGb but they're still a third similar! { // only give a direction response to a good match // Scale signal according to distance downrange intensity = cone0.Distance(range); // compute direction float a0 = 1f - angle0 / halfAngle; float a1 = 1f - angle1 / halfAngle; direction = (a1 - a0) / 2f + 0.5f; if (focusDirection < 0f) focusDirection = 0f; else if (focusDirection > 1f) focusDirection = 1f; } // If this stimulus comes from a different source to our present focus of attention // ignore it unless it is stronger (in which case, shift attention to it) if ((stim.From != focusObject) // Is this object different from our present focus of attention && (intensity < focusIntensity)) // and weaker? return true; // ignore it focusObject = stim.From; // Shift attention if necessary (if found stronger source) focusIntensity = intensity; // record the new signal strength focusDirection = direction; lossOfSignal = LOSSOFSIGNALAFTER; // and reset the loss-of-signal timers fading = false; // Calculate the signal entering each sensor if (angle0 < halfAngle) // if the source is visible from sensor0 signal0 = intensity * (1f - angle0 / halfAngle); // scale signal by deviation from sensor's midline if (angle1 < halfAngle) // otherwise leave as zero signal1 = intensity * (1f - angle1 / halfAngle); // Repeat for sensor1 SetOutputs(); // Write the two cell outputs } } return true; } return false; }
/// <summary> /// Emit a specialised broadcast stimulus (other than sounds, disturbances, etc.) /// </summary> /// <param name="type">Stimulus type, e.g. "sound"</param> /// <param name="hotspot">hotspot emitting stimulus, or -1 to emit from the cell's axis</param> /// <param name="range">distance over which stimulus travels</param> /// <param name="angle">cone of transmission - half-angle from hotspot or cell's normal</param> /// <param name="param0">first stimulus param (type-dependent)</param> /// <param name="param1"></param> /// <param name="param2"></param> /// <param name="param3"></param> protected void EmitStimulus(string type, int hotspot, float range, float angle, Object param0, Object param1, Object param2, Object param3) { // Create the stimulus Stimulus stimulus = new Stimulus((IDetectable)owner, ((IDetectable)owner).ReqLocation(), range, type, param0, param1, param2, param3); // Send it to each organism in range along the cone of transmission SensorItem[] recipients = owner.GetObjectsInRange(hotspot, range, true, false, false); foreach (SensorItem recipient in recipients) { if (recipient.Angle() <= angle) recipient.Object.ReceiveStimulus(stimulus); } }
/// <summary> /// Common stimulus: Emit an omnidirectional ripple of water disturbance, to show that a cell has radically changed shape. /// Use when a muscle twitches rapidly, jaws clench, etc. Whole-body movements should be detected passively; this is just a shockwave in the water /// that can be picked up by a hairy cell, like the lateral line in fish. /// Usually best called from a SlowUpdate() whenever the cell (e.g. its JointOutput) has changed more than a certain amount since the last update. /// Note: the sub engines could emit disturbances, which could scare away creatures /// </summary> /// <param name="range">The intensity of the stimulus and hence its range</param> protected void Disturbance(float range) { Stimulus stimulus = new Stimulus((IDetectable)owner, ((IDetectable)owner).ReqLocation(), range, "disturbance", null, null, null, null); SensorItem[] recipients = owner.GetObjectsInRange(-1, range, true, false, false); foreach (SensorItem recipient in recipients) { recipient.Object.ReceiveStimulus(stimulus); } }
/// <summary> We've been sent a Stimulus that our basic Cell object doesn't understand. /// Override this method to handle any stimuli that our particular cell type understands. /// This mechanism allows new cell types to declare new stimulus types. Note however that /// existing cell types won't have any handlers for these new stimuli. (On the other hand /// it's possible to define handlers in advance of any cell types that know how to emit a /// particular stimulus, making them "stimulus-ready"!) /// <param name="stimulus">The stimulus information</param> /// <returns>Return true if the stimulus was handled</returns> public virtual bool ReceiveStimulus(Stimulus stim) { return false; }
public override bool ReceiveStimulus(Stimulus stim) { switch (stim.Type) { // Potentially picked up a disturbance case "disturbance": SensorItem result = owner.TestStimulusVisibility(0, range, stim); // see if the source is within our acceptance angle if (result.Angle()<HALFANGLE) { Output(0,result.Distance(range)); // if so, output a signal as a fraction of the source range } return true; } Output(0,0); // if no valid sensation, clear the signal return false; }
/// <summary> We've been sent a Stimulus that our basic Cell object doesn't understand. /// This overload responds to the "bioluminescence" stimulus /// Parameter 0 will be a ColorValue containing the bioluminescent cell's current anim colour /// <param name="stimulus">The stimulus information</param> /// <returns>Return true if the stimulus was handled</returns> public override bool ReceiveStimulus(Stimulus stim) { if (stim.Type == "bioluminescence") { // Find out if the sender is within range/sight of our hotspot SensorItem sender = owner.TestStimulusVisibility(0, range, stim); float dist = sender.Distance(); if (dist<range) { float angle = sender.Angle(); if (angle < halfAngle) { // Parameter 0 will be a ColorValue containing the bioluminescent cell's current anim colour ColorValue light = (ColorValue)stim.Param0; // Compare the light to our filter colour float r1 = light.Red - r; // difference in RGB between filter and cell float g1 = light.Green - g; float b1 = light.Blue - b; float signal = 1f - (float)Math.Sqrt((r1 * r1 + g1 * g1 + b1 * b1) / 3f); // least squares measure of similarity (1=identical) signal *= 1f - angle / halfAngle; // scale by deviation from mid-line (so objects straight ahead have more effect) // NOTE: Removed this because bioluminescent cells are obviously tiny at long range //float apparentSize = sender.AngleSubtended(); // scale by angle subtended (depends on size and distance) //if (apparentSize < 0) apparentSize = 0; //signal *= apparentSize; // Add this signal to that waiting for inclusion in the output incoming += signal; } } return true; } else return false; }