diff --git a/src/extensions/botch/index.js b/src/extensions/botch/index.js index 8af1d6c6c5d..74a4be046de 100644 --- a/src/extensions/botch/index.js +++ b/src/extensions/botch/index.js @@ -38,6 +38,7 @@ class Scratch3Botch { // utils this.botchUtil = new BotchUtil(this.runtime); this.currentOrgCounter = 0; + this.originalOrg = null; // this is the clone that hide the original when set as organism // since that the project is loaded at the startup // for some reasons the storage is not already defined and it needs to @@ -330,7 +331,11 @@ class Scratch3Botch { } this.organismMap = new Map(); - this.organismMap.set(util.target.id, new Organism(util.target)); + const org = new Organism(util.target); + this.organismMap.set(util.target.id, org); + + org.currentName = this.currentOrgCounter.toString(); + util.target.setVisible(false); this.createOrganismClone(util.target, 0); @@ -396,7 +401,6 @@ class Scratch3Botch { // newClone.setSize((Math.random() * 100) + 40); if (i === 0) { // the first clone is placed where is the original newClone.setXY(target.x, target.y); - target.setCostume(org.target.currentCostume); // don't know why this does not work! } else { // place the new clone in a random position const stageW = this.runtime.constructor.STAGE_WIDTH; @@ -409,6 +413,10 @@ class Scratch3Botch { newClone.setCustomState('storedMd5', p.md5); const state = this.getBotchState(newClone); state.type = Scratch3Botch.ORGANISM_TYPE; + + if (target.isOriginal) { + this.originalOrg = org; + } } } @@ -537,15 +545,17 @@ class Scratch3Botch { reproduceChild (args, util) { const mr = MathUtil.clamp(Cast.toNumber(args.MR), 0, 100); if (this.organismMap.size > 0 && this.organismMap.get(util.target.id)) { - const org = this.organismMap.get(util.target.id); - if (org && !org.target.isOriginal) { // Only the clones are managed - if (org.health > 0) { - const newOrg = org.clone(mr); + let org = this.organismMap.get(util.target.id); + if (util.target.isOriginal) { + org = this.originalOrg; + } + if (org) { + if (org.health >= 0) { const newClone = this.botchUtil.createClone(org.target); if (newClone) { this.runtime.addTarget(newClone); + const newOrg = org.clone(mr, newClone); newClone.clearEffects(); - newOrg.target = newClone; // assign the new target to new organism org.childNumber++; newOrg.currentName = `${org.currentName}.${org.childNumber}`; const p = this.storeSprite(newClone.id, newOrg.currentName); @@ -558,15 +568,14 @@ class Scratch3Botch { } } else if (this.enemiesMap.size > 0 && this.enemiesMap.get(util.target.id)) { const enemy = this.enemiesMap.get(util.target.id); - if (enemy && !enemy.target.isOriginal) { - const newOrg = enemy.clone(mr); + if (enemy) { const newClone = this.botchUtil.createClone(enemy.target); if (newClone) { this.runtime.addTarget(newClone); + const newOrg = enemy.clone(mr, newClone); newClone.clearEffects(); - newOrg.target = newClone; + this.enemiesMap.set(newClone.id, newOrg); } - this.enemiesMap.set(newClone.id, newOrg); } } } diff --git a/src/extensions/botch/organism.js b/src/extensions/botch/organism.js index f201e65451e..9836feb69d3 100644 --- a/src/extensions/botch/organism.js +++ b/src/extensions/botch/organism.js @@ -20,7 +20,7 @@ class Organism { * @param {RenderedTarget} target_ target (sprite) * @param {number} mass_ mass of the vehicle * @param {number} maxForce_ max force of the vehicle - * @param {string} svgPoints_ svg points of the organism + * @param {Array} svgPoints_ svg points of the organism * @param {number} mutation how the organism can mutate [0 - 100] * @param {number} foodAttraction food attraction * @param {number} foodSight food sight @@ -57,8 +57,8 @@ class Organism { this.versionName = ''; this.max_att = 5; this.max_perception = 150; - this.max_size = 140; - this.min_size = 30; + this.max_size = 110; + this.min_size = 50; this.currentName = ''; this.childNumber = 0; // "dna" parameter @@ -74,14 +74,18 @@ class Organism { const sizeScaled = MathUtil.scale(mutation, 0, 100, 0, this.max_size); this.foodAttraction = foodAttraction; this.foodAttraction += this.botchUtil.rdn(-attScaled, attScaled); + this.foodAttraction = MathUtil.clamp(this.foodAttraction, -this.max_att, this.max_att); this.foodSight = foodSight; this.foodSight += this.botchUtil.rdn(-perScaled, perScaled); + this.foodSight = MathUtil.clamp(this.foodSight, 0, this.max_perception); this.enemyAttraction = enemyAttraction; this.enemyAttraction += this.botchUtil.rdn(-attScaled, attScaled); + this.enemyAttraction = MathUtil.clamp(this.enemyAttraction, -this.max_att, this.max_att); this.enemySight = enemySight; this.enemySight += this.botchUtil.rdn(-perScaled, perScaled); + this.enemySight = MathUtil.clamp(this.enemySight, 0, this.max_perception); this.size = orgSize; - this.size += this.botchUtil.rdn(-sizeScaled / 2, sizeScaled / 2); + this.size += this.botchUtil.rdn(-sizeScaled / 3, sizeScaled / 3); this.size = MathUtil.clamp(this.size, this.min_size, this.max_size); } else { this.foodAttraction = this.botchUtil.rdn(-this.max_att, this.max_att); // food attraction @@ -93,7 +97,7 @@ class Organism { // if is not defined, do not generate if (svgPoints_) { - this.svg = this.svgGen.generateOrgSVG2( + this.svg = this.svgGen.generateOrgSVG3( 100, this.foodAttraction, this.enemyAttraction, this.max_att, this.foodSight, this.enemySight, this.max_perception, svgPoints_, mutation); this.botchUtil.uploadCostumeEdit(this.svg, this.target.id); @@ -105,6 +109,9 @@ class Organism { } */ this.target.setSize(this.size); + } else { + this.svg = this.svgGen.generateOrgSVG3(100, this.foodAttraction, this.enemyAttraction, this.max_att, + this.foodSight, this.enemySight, this.max_perception); } // values found empirically @@ -129,11 +136,11 @@ class Organism { * @since botch-0.2 */ stepOrganism (enemiesMap) { - this.boundaries( - this.runtime.constructor.STAGE_WIDTH - 50, - this.runtime.constructor.STAGE_HEIGHT - 50); this.refreshArgs(this.mass, this.maxForce); this.behaveGeneralOrganism(enemiesMap); + this.boundaries( + this.runtime.constructor.STAGE_WIDTH - 30, + this.runtime.constructor.STAGE_HEIGHT - 30); this.update(); this.breathe(); } @@ -158,12 +165,8 @@ class Organism { * assign the new generated costume to the target */ assignOrgCostume () { - this.svg = this.svgGen.generateOrgSVG2(100, this.foodAttraction, this.enemyAttraction, this.max_att, - this.foodSight, this.enemySight, this.max_perception); this.botchUtil.uploadCostumeEdit(this.svg, this.target.id); - // this.area = this.svgGen.calcOrgMass(); this.target.setSize(this.size); - // this.health = MathUtil.scale(this.area, 2000, (this.svgGen.width) ** 2, 1, 3); } /** @@ -394,11 +397,12 @@ class Organism { * Create a clone of itself "Parthenogenesis" * when called it return always a new child * @param {number} mutation how the organism will mutate [0 - 100] + * @param {target} target new clone target * @returns {Organism} new copy Organism * @since botch-0.2 */ - clone (mutation) { - const newOrg = new Organism(this.target, 1, 0.5, this.svgGen.getOrgPoints(), mutation, + clone (mutation, target) { + const newOrg = new Organism(target, this.mass, 0.5, this.svgGen.getOrgPoints(), mutation, this.foodAttraction, this.foodSight, this.enemyAttraction, this.enemySight, this.size); return newOrg; } @@ -518,7 +522,7 @@ class Organism { desired.normalize(); desired.mult(this.maxSpeed); const steer = Vector2.sub(desired, this.velocity); - steer.limit(this.maxForce); + // steer.limit(this.maxForce); this.applyForce(steer); } } diff --git a/src/extensions/botch/svg-generator.js b/src/extensions/botch/svg-generator.js index 227018c9c31..5b29f5c5836 100644 --- a/src/extensions/botch/svg-generator.js +++ b/src/extensions/botch/svg-generator.js @@ -107,7 +107,7 @@ class SVGgen { * @param {number} magR max poison or food attraction * @param {number} foodS food distance * @param {number} poisonS poison distance - * @param {number} magA max poison or food distance + * @param {number} magD max poison or food distance * @param {Array} parentPoints the points of the parent * @param {number} mutation how the organism svg will be different * @returns {string} the svg @@ -122,109 +122,111 @@ class SVGgen { * . * fa piuttosto schifo da vedere come funzione... */ - generateOrgSVG2 (dim, foodR, poisonR, magR, foodS, poisonS, magA, parentPoints, mutation) { + generateOrgSVG3 (dim, foodR, poisonR, magR, foodS, poisonS, magD, parentPoints, mutation) { const f = MathUtil.scale(foodR, -magR, magR, 0, 15); const p = MathUtil.scale(poisonR, -magR, magR, 0, 15); - const fl = MathUtil.scale(foodS, 0, magA, 0, 50); - const pl = MathUtil.scale(poisonS, 0, magA, 0, 50); + const fl = MathUtil.scale(foodS, 0, magD, 0, 50); + const pl = MathUtil.scale(poisonS, 0, magD, 0, 50); const antennaWidth = 5; - const controlPointF = MathUtil.scale(foodS, 0, magA, 0, 50); - const controlPointP = MathUtil.scale(poisonS, 0, magA, 0, 50); + const controlPointF = MathUtil.scale(foodS, 0, magD, 0, 10); + const controlPointP = MathUtil.scale(poisonS, 0, magD, 0, 10); + const margin = 15; + // resize the canvas to be a square this.width = dim; this.height = dim; - - const margin = 15; - const w1 = (this.width / 2) - margin; - const w2 = (this.width / 2) + margin; + const maxMargin = Math.max(fl + f, pl + p); + this.width += maxMargin * 2; + this.height = this.width; + + const w0 = (this.width / 2) + (dim / 2); + const w1 = (this.width / 2) - (dim / 2); + const wm0 = (this.width / 2) + margin; + const wm1 = (this.width / 2) - margin; + + // const h0 = (dim / 2); // the new points differ by -min +max from the parent point - const mutQuantity = MathUtil.scale(mutation, 0, 100, 0, 15); let p1; let p2; let p3; let p4; let c1; let c2; let c3; let c4; if (parentPoints) { - if (mutQuantity > 0) { + const mutQuantity = MathUtil.scale(mutation, 0, 100, 0, 10); // generate the 4 points on the diagonal - const ta = MathUtil.clamp( - parentPoints[0][0].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), 0, w1); - p1 = new Vector2(ta, ta); + const ta = MathUtil.clamp( + parentPoints[0][0].x + this.rdn(-mutQuantity, mutQuantity), w1, wm1); + p1 = new Vector2(ta, ta); - const tb = MathUtil.clamp( - parentPoints[0][1].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), w2, this.width - margin); - p2 = new Vector2(tb, -tb + this.height); + const tb = MathUtil.clamp( + parentPoints[0][1].x + this.rdn(-mutQuantity, mutQuantity), wm0, w0); + p2 = new Vector2(tb, -tb + this.height); - const tc = MathUtil.clamp( - parentPoints[0][2].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), w2, this.width - margin); - p3 = new Vector2(tc, tc); + const tc = MathUtil.clamp( + parentPoints[0][2].x + this.rdn(-mutQuantity, mutQuantity), wm0, w0); + p3 = new Vector2(tc, tc); - const td = MathUtil.clamp( - parentPoints[0][3].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), 0, w1); - p4 = new Vector2(td, -td + this.height); + const td = MathUtil.clamp( + parentPoints[0][3].x + this.rdn(-mutQuantity, mutQuantity), w1, wm1); + p4 = new Vector2(td, -td + this.height); - // generate the 4 control points - const va = MathUtil.clamp( - parentPoints[1][0].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), 0, this.width); - const v2a = va < this.width / 2 ? - Math.floor(this.rdn(0, va)) : Math.floor(this.rdn(0, -va + this.height)); - c1 = new Vector2(va, v2a); + // generate the 4 control points + const va = MathUtil.clamp( + parentPoints[1][0].x + this.rdn(-mutQuantity, mutQuantity), w1, w0); + const minA = Math.min(p1.y, p2.y) - 10; + const v2a = MathUtil.clamp( + parentPoints[1][1].y + this.rdn(-mutQuantity, mutQuantity), w1, minA); + c1 = new Vector2(va, v2a); - const vb = MathUtil.clamp( - parentPoints[1][1].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), this.width / 2, this.width); - const v2b = Math.floor(this.rdn(-vb + this.height, vb)); - c2 = new Vector2(vb, v2b); + const maxB = Math.max(p2.x, p3.x) + 10; + const vb = MathUtil.clamp( + parentPoints[1][1].x + this.rdn(-mutQuantity, mutQuantity), maxB, w0); + const v2b = MathUtil.clamp( + parentPoints[1][1].y + this.rdn(-mutQuantity, mutQuantity), w1, w0); + c2 = new Vector2(vb, v2b); - const vc = MathUtil.clamp( - parentPoints[1][2].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), 0, this.width); - const v2c = vc < this.width / 2 ? - Math.floor(this.rdn(-vc + this.height, this.height)) : Math.floor(this.rdn(vc, this.height)); - c3 = new Vector2(vc, v2c); + const vc = MathUtil.clamp( + parentPoints[1][2].x + this.rdn(-mutQuantity, mutQuantity), 0, this.width); + const maxC = Math.max(p3.y, p4.y) + 10; + const v2c = MathUtil.clamp( + parentPoints[1][2].y + this.rdn(-mutQuantity, mutQuantity), maxC, w0); + c3 = new Vector2(vc, v2c); - const vd = MathUtil.clamp( - parentPoints[1][3].x + Math.floor(this.rdn(-mutQuantity, mutQuantity)), 0, this.width / 2); - const v2d = Math.floor(this.rdn(vd, -vd + this.height)); - c4 = new Vector2(vd, v2d); - } else { - p1 = parentPoints[0][0]; - p2 = parentPoints[0][1]; - p3 = parentPoints[0][2]; - p4 = parentPoints[0][3]; - c1 = parentPoints[1][0]; - c2 = parentPoints[1][1]; - c3 = parentPoints[1][2]; - c4 = parentPoints[1][3]; - } + const minD = Math.min(p1.x, p4.x) - 10; + const vd = MathUtil.clamp(parentPoints[1][3].x + this.rdn(-mutQuantity, mutQuantity), w1, minD); + const v2d = MathUtil.clamp( + parentPoints[1][3].y + this.rdn(-mutQuantity, mutQuantity), w1, w0); + c4 = new Vector2(vd, v2d); } else { // generate the 4 points on the diagonal - const ta = Math.floor(this.rdn(0, w1)); + const ta = Math.floor(this.rdn(w1, wm1)); p1 = new Vector2(ta, ta); - const tb = Math.floor(this.rdn(w2, this.width - margin)); + const tb = Math.floor(this.rdn(wm0, w0)); p2 = new Vector2(tb, -tb + this.height); - const tc = Math.floor(this.rdn(w2, this.width - margin)); + const tc = Math.floor(this.rdn(wm0, w0)); p3 = new Vector2(tc, tc); - const td = Math.floor(this.rdn(0, w1)); + const td = Math.floor(this.rdn(w1, wm1)); p4 = new Vector2(td, -td + this.height); // generate the 4 control points - const va = Math.floor(this.rdn(0, this.width)); + const vaX = Math.floor(this.rdn(w1, w0)); const minA = Math.min(p1.y, p2.y) - 10; - const v2a = Math.floor(this.rdn(0, minA)); - c1 = new Vector2(va, v2a); + const v2a = Math.floor(this.rdn(w1, minA)); + c1 = new Vector2(vaX, v2a); const maxB = Math.max(p2.x, p3.x) + 10; - const vb = Math.floor(this.rdn(maxB, this.width)); + const vb = Math.floor(this.rdn(maxB, w0)); const v2b = Math.floor(this.rdn(-vb + this.height, vb)); c2 = new Vector2(vb, v2b); - const vc = Math.floor(this.rdn(0, this.width)); + const vc = Math.floor(this.rdn(w1, w0)); const maxC = Math.max(p3.y, p4.y) + 10; - const v2c = Math.floor(this.rdn(maxC, this.height)); + const v2c = Math.floor(this.rdn(maxC, w0)); c3 = new Vector2(vc, v2c); const minD = Math.min(p1.x, p4.x) - 10; - const vd = Math.floor(this.rdn(0, minD)); + const vd = Math.floor(this.rdn(w1, minD)); const v2d = Math.floor(this.rdn(vd, -vd + this.height)); c4 = new Vector2(vd, v2d); } @@ -232,10 +234,8 @@ class SVGgen { this.svgOrgPoints = [p1, p2, p3, p4]; this.controlOrgPoints = [c1, c2, c3, c4]; - this.checkBordersOrg(f + fl, p + pl); - - const rgXf = foodR > 0 ? p2.x + fl : p2.x - fl; - const rgXp = poisonR > 0 ? p3.x + pl : p3.x - pl; + const rgXf = p2.x + fl; + const rgXp = p3.x + pl; this.svg = `` + ``; - if (foodR > 0) { - this.svg += `` + ``; - } else { - this.svg += `` + - ``; - } + `r="${f}" stroke="${this.colorLuminance(this.color, -0.3)}" ` + + `stroke-width="1" fill="url(#RadialGradient2)" />`; + - if (poisonR > 0) { - this.svg += `` + ``; - } else { - this.svg += `` + - ``; - } + `r="${p}" stroke="${this.colorLuminance(this.color, -0.3)}" ` + + `stroke-width="1" fill="url(#RadialGradient3)" />`; this.svg += ``;