memoscan
← all memos

Memo 0x022a2ea3…d82570 on Ethereum

function bud(x,y,rs){ C.push(x,y,80,'O',FC(shade(P.bud,rs)),'flowers'); C.push(x,y-GRID*0.6,81,'.',FC(shade(mix(P.bud,P.springRed,springFlush),rs)),'flowers'); } function sprout(rs,rx){ if(1-smooth(0.05,0.20,m) <= 0.02) return; const g=smooth(0,0.18,m), hLen=(52+46*g)*S; let dir=UP+D.lean*1.2, pos=[rx,by-3]; const steps=Math.max(3,Math.ceil(hLen/GRID)); for(let i=1;i<=steps;i++){ const u=i/steps; dir+=Math.sin(u*2.6+D.lean*4)*0.045; pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; C.push(pos[0],pos[1],50,dirSym(Math.cos(dir),Math.sin(dir)),shade(bark,rs),'stem'); } if(bareWinter){ bud(pos[0],pos[1],rs); return; } // juvenile seedling carries a small SPRAY of first palmate leaves (not a bare whip with one leaf) const nl=1+Math.round(clamp(g,0,1)*2.4); // 1–3 first leaves for(let k=0;k<nl;k++){ const a=UP + (k-(nl-1)/2)*0.52 + (rs()*2-1)*0.16; leafSpray(pos[0],pos[1], a, (6.5+6*g)*S, rs); } if(g>0.5){ const md=steps>3?Math.floor(steps*0.6):2, mp=[rx+Math.cos(UP)*GRID*md, by-3+Math.sin(UP)*GRID*md]; // a lower pair along the stem for(const sd of [1,-1]) if(rs()<0.7) leafSpray(mp[0],mp[1], UP+sd*0.7, (5+5*g)*S, rs); } } const growWin=0.12, maxOrder=D.maxOrder; let budget=8000; const chain=(1-Math.pow(D.taperLen,maxOrder+1))/(1-D.taperLen), baseRun=((by-300)/chain)*D.stature*(0.9+0.1*v)*S; function grow(x,y,head,vigor,order,birthM,pid,sideSeq){ if(budget<=0) return; const grown=clamp((m-birthM)/growWin,0,1); if(grown<=0) return; budget--; const rs=rng('mb', seed, pid); let run=baseRun*Math.pow(D.taperLen,order)*(0.82+0.36*rs())*grown; if(order===0) run*=0.82; const steps=Math.max(1,Math.ceil(run/GRID)), woodM=smooth(0.10,0.85,m), baseThick=(maxOrder-order)/maxOrder, bz=54-order; const f1=0.5+1.4*rs(), f2=1.1+2.2*rs(), ph1=rs()*6.28, ph2=rs()*6.28, gnarlA=D.gnarl*0.045*(1+order*0.12), bears=order>=2 ? D.leafiness : 0; const canChild = grown>=1 && order<maxOrder; const nLat = order===0 ? 2+Math.floor(rs()*3) : clamp(Math.round(D.lateralN*(1-0.35*D.dominance)+gauss(rs)*0.9),0, order<2?3:2); const lo=order===0?0.22:0.30, hi=0.93, pts=[], sides=[]; let lastSide=rs()<0.5?1:-1; for(let c=0;c<nLat;c++){ pts.push(clamp(lerp(lo,hi,(c+0.5)/Math.max(nLat,1)) + (rs()*2-1)*(0.6/Math.max(nLat,1)), 0.1, 0.95)); if(order===0 || rs()<0.72) lastSide=-lastSide; sides.push(lastSide); } pts.sort((a,b)=>a-b); let dir=head, pos=[x,y], ci=0; for(let i=1;i<=steps;i++){ const u=i/steps; const photo=lerp(0.12,0.035,clamp(order/3,0,1))*Math.max(0,1-order/3.2), grav=0.12*D.droop*smooth(1.4,maxOrder,order)*(0.25+0.75*u); dir += angTo(dir,UP)*photo + angTo(dir,DOWN)*grav + gnarlA*(Math.sin(i*0.5*f1+ph1)+0.5*Math.sin(i*0.3*f2+ph2)); const exposure=clamp((by-pos[1])/600,0,1); dir += D.windDir*D.windStr*0.06*exposure*(0.35+0.65*order/maxOrder); const dx=Math.cos(dir), dy=Math.sin(dir); pos=[pos[0]+dx*GRID, pos[1]+dy*GRID]; const sym=dirSym(dx,dy); C.push(pos[0],pos[1], bz, sym, shade(bark,rs), 'stem'); const px=-dy, py=dx; const thick=clamp(Math.round(baseThick*baseThick*1.9*woodM*(0.7+0.3*v)*(1-0.5*u)*smooth(0.12,0.85,grown)),0,2); for(let wq=1;wq<=thick;wq++){ C.push(pos[0]+px*wq*GRID, pos[1]+py*wq*GRID, bz, sym, shade(barkDk,rs), 'stem'); C.push(pos[0]-px*wq*GRID, pos[1]-py*wq*GRID, bz, sym, shade(barkDk,rs), 'stem'); } if(order>=2 && u>0.1 && i%3===1){ if(bareWinter){ if(rs()<0.22) bud(pos[0],pos[1],rs); } else if(rs()<bears){ const side=((sideSeq+(i/2|0))%2===0)?1:-1; leafSpray(pos[0],pos[1], Math.atan2(py*side,px*side), (12+11*m)*(0.85+0.3*rs())*S, rs); } if(springFlush>0.3 && rewards>0.25 && rs()<0.05) flowerCluster(pos[0],pos[1],rs); if((fadeAut>0.2 || (springFlush<0.2 && leafOut>0.6 && doy>150)) && rewards>0.25 && rs()<0.05*D.samaraAb) samara(pos[0],pos[1],rs); } while(canChild && ci<pts.length && u>=pts[ci] && budget>0){ const side=sides[ci], div=(order===0?(0.55+0.55*D.reach):D.divergence*(0.7+0.7*rs())*D.reach); let la=dir + side*div + gauss(rs)*0.14; la += angTo(la,UP)*(order===0?0.05:0.08)*(1-D.reach*0.3); const cv=vigor*(order===0?(0.72+0.26*rs()):(0.45+0.4*(1-D.dominance))*(0.8+0.3*rs())); grow(pos[0],pos[1], la, cv, order+1, birthM+growWin, pid*7+ci+2, sideSeq+ci+1); ci++; } } if(canChild && budget>0){ const leadV=vigor*lerp(0.38,0.9,D.dominance); grow(pos[0],pos[1], dir+angTo(dir,UP)*(order<=1?0.26:0.12)+gauss(rs)*0.06, leadV, order+1, birthM+growWin, pid*7+1, sideSeq+1); } else if(grown>=1){ if(bareWinter){ bud(pos[0],pos[1],rs); } else { leafSpray(pos[0],pos[1], dir, (15+13*m)*S, rs); if(rs()<0.5) leafSpray(pos[0],pos[1], dir+0.7, (13+11*m)*S, rs); } } else if(order>=2 && !bareWinter){ leafSpray(pos[0],pos[1], dir, (13+10*m)*S, rs); } } const rootX=cx - D.windDir*D.windStr*48*S; sprout(rng('sprout',seed), rootX); grow(rootX, by-3, UP+D.lean*0.6+D.windDir*D.windStr*0.07, 1, 0, 0, 1, 0); } // ===================== SETTING: maples are specimen trees (mostly Solo) ===================== const roll=rng('setting',st.seed)(); const nC = roll<0.5?0 : roll<0.82?1 : 2+Math.floor(rng('fn',st.seed)()*2); const comps=[]; for(let i=0;i<nC;i++){ const cseed='0x'+(hashStr(st.seed+'|c'+i)>>>0).toString(16), rc=rng('comp',st.seed,i), side=(i%2===0?1:-1), f=i/Math.max(1,nC); comps.push({seed:cseed, artSeed:cseed, cx:baseX+side*(170+i*70+rc()*40), by:baseY-(4+i*10+rc()*16), S:lerp(0.58,0.42,f)*(0.9+0.2*rc()), dim:lerp(0.42,0.60,f), m:clamp(m+(rc()*2-1)*0.06,0.04,1), density, v, sen, rewards, depth:f}); } comps.sort((a,b)=>b.depth-a.depth).forEach(paintMaple); paintMaple({seed:st.seed, artSeed:st.artSeed, cx:baseX, by:baseY, S:1.0, dim:0, m, density, v, sen, rewards}); }, traits(st,env){ const D=mapleDNA(st.seed); const roll=rng('setting',st.seed)(); const setting = roll<0.5?'Solo' : roll<0.82?'Pair' : 'Grove'; const doy=(((env.now+70)%365)+365)%365; const colour = (doy<56||doy>352)?'Bare' : doy<96?'Spring red' : doy<240?'Green' : doy<300?'Turning' : 'Autumn fire'; const cult = D.cultivar>0.55?'Purple' : D.cultivar>0.25?'Bronze' : 'Green'; const auh = D.autumnHue>0.6?'Scarlet' : D.autumnHue>0.3?'Orange' : 'Gold'; return [ ['Setting', setting], ['Colour now', colour], ['Lobes', D.lobes], ['Cultivar', cult], ['Autumn', auh], ['Wind', D.windStr<0.12?'Still':D.windStr<0.40?'Breezy':D.windStr<0.70?'Windswept':'Storm-bent'], ]; } })); })(); ; /* species/poppy.js */ /* poppy — species index 1. GENERATED from prototypes/field-poppy.html by extract.js. Do not edit here — edit the prototype and re-run `npm run extract`. The prototype's mount() call is redirected to SeedlingRenderer.registerSpecies via the local shim. */ (function () { const mount = (spec) => SeedlingRenderer.registerSpecies(1, spec); /* Field Poppy — Papaver rhoeas. A leafy clump of deeply pinnately-lobed (fern-like) leaves, out of which slender hairy stems BOLT and each runs its own lifecycle: a nodding green bud → straightens → 4 papery scarlet petals with a dark blotch + stamen ring → a ribbed pepper-pot seed CAPSULE. An older plant carries all stages at once. Coherent WIND leans the whole plant. Monocarpic. Scatters in drifts → a rare Field carpet. Ported to the shared core. */ const SP = window.SEEDLING_SPECIES.find(x=>x.key==='poppy'); mount(Object.assign(window.SEEDLING_toSpec(SP), { title:'Field Poppy', sub:'papaver v2', senFrom:0.80, note:'Lobed fern-like rosette · slender hairy stems bolt and bloom · nodding bud → 4 papery scarlet petals → ribbed capsule (all stages at once) · wind-leaned · scatters into drifts/fields.', PAL:{ soil:'#6f5128', dry:'#7a5a30', stem:'#5E7B3A', stemDry:'#9A8A52', leafDark:'#46662F', leafMid:'#6E8B4E', leafLight:'#8BA86A', petal:'#E0382C', petal2:'#C42718', petalEdge:'#F0664E', blotch:'#2A0E07', center:'#1D1208', stamen:'#241606', bud:'#7E924E', capsule:'#8FA05C', capsuleDry:'#A6924E', pore:'#241a0c' }, stageName(st,env){ if(st.diedAt>0)return'Sealed'; const m=env.m; if(env.ageF>0.85)return'Senescent'; if(m<0.12)return'Sprout'; if(m<0.40)return'Rosette'; if(m<0.58)return'Budding'; if(m<0.80)return'Blooming'; return'Seeding'; }, draw(st, C, env){ const {m,v,sen,season,browny,density,rewards,ageF,baseX,baseY}=env, P=this.PAL; const bgC = BACKDROPS[backdropFor(st.seed)]; const seasonBloom = season==='Summer'?1 : season==='Spring'?0.9 : season==='Autumn'?0.7 : 0.55; function poppyDNA(seed){ const dd=rng('dna',seed), g=()=>(dd()+dd()+dd()-1.5)/1.5; return { stems:0.7+0.9*dd(), spread:26+26*dd(), stature:0.84+0.30*dd(), waviness:0.5+1.0*dd(), petalRag:0.10+0.30*dd(), petalHue:g()*0.5, leafCut:0.55+0.45*dd(), bristly:0.5+0.5*dd(), windDir:dd()<0.5?1:-1, windStr:Math.pow(dd(),2.3) }; } // ===================== one poppy plant (repeated to scatter into a drift/field) ===================== function paintPoppy(I){ const {seed, artSeed, cx, by, S, fade, m, density, v, sen, browny, rewards} = I; const FC = c => fade>0 ? mix(c, bgC, fade) : c; const ra=rng('struct',artSeed), D=poppyDNA(seed); const stemC=FC(mix(P.stem,P.stemDry,Math.max((1-v)*0.5,sen))); const leafD=FC(mix(P.leafDark,P.stemDry,browny)), leafM=FC(mix(P.leafMid,P.stemDry,browny)), leafL=FC(mix(P.leafLight,P.stemDry,browny*0.9)); const petalC=FC(mix(P.petal,P.petal2,clamp(-D.petalHue,0,1)*0.7)), petalWarm=FC(mix(mix(P.petal,P.petal2,clamp(-D.petalHue,0,1)*0.7),'#F08A3C',clamp(D.petalHue,0,1)*0.6)); const capC=FC(mix(P.capsule,P.capsuleDry,Math.max(sen,browny*0.55))); const greenOf=rs=>shade(mix(leafM, rs()<0.5?leafD:leafL, rs()),rs); const windLean=D.windDir*D.windStr*0.42, rootX=cx - D.windDir*D.windStr*46*S; function lobedLeaf(bx,byy,ang,len,rl){ const steps=Math.max(3,Math.ceil(len/GRID)), droop=clamp(sen+(1-v)*0.4,0,1)*0.7; let dx=Math.cos(ang),dy=Math.sin(ang); dx=lerp(dx,0,droop); dy=lerp(dy,1,droop); const n=Math.hypot(dx,dy)||1; dx/=n; dy/=n; let dir=Math.atan2(dy,dx), pos=[bx,byy]; for(let s=1;s<=steps;s++){ const u=s/steps; dir += Math.sin(u*3.0+rl()*6)*0.04; const cd=[Math.cos(dir),Math.sin(dir)]; pos=[pos[0]+cd[0]*GRID, pos[1]+cd[1]*GRID]; C.push(pos[0],pos[1], 42, dirSym(cd[0],cd[1]), shade(leafM,rl), 'leaves'); const px=-cd[1], py=cd[0], ev=Math.sin(Math.PI*Math.min(u,0.999)), lobeLen=GRID*(0.8+(2.4*D.leafCut)*ev); if(s%2===0 && ev>0.18){ for(const sd of [1,-1]){ let la=Math.atan2(py*sd,px*sd); la += angTo(la,dir)*0.30; const ll=lobeLen*(0.7+0.5*rl()), lsteps=Math.max(1,Math.round(ll/GRID)); for(let t=1;t<=lsteps;t++){ const lp=[pos[0]+Math.cos(la)*GRID*t, pos[1]+Math.sin(la)*GRID*t], edge=t>=lsteps; C.push(lp[0],lp[1], 41, edge?'X':dirSym(Math.cos(la),Math.sin(la)), edge?shade(leafD,rl):greenOf(rl), 'leaves'); } } } } } const foliageF=(1-0.5*sen), axisH=(78+150*m)*(0.85+0.30*D.stature)*foliageF*S; { const rl=rng('axis',seed); let dir=UP+windLean*0.4, pos=[rootX,by-4]; const steps=Math.max(2,Math.ceil(axisH/GRID)); for(let s=1;s<=steps;s++){ const u=s/steps; dir += Math.sin(u*2+rl()*3)*0.02 + windLean*0.03*u; pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID]; C.push(pos[0],pos[1],46,dirSym(Math.cos(dir),Math.sin(dir)),shade(stemC,rl),'stem'); } } const nFolF=clamp(3 + density*6*foliageF + (rng('nl',seed)()*2-1)*0.6, 2.5, 10), nFol=Math.ceil(nFolF); for(let i=0;i<nFol;i++){ const rl=rng('leaf',seed,i), side=i%2?1:-1, emerge=i===nFol-1?clamp(nFolF-(nFol-1),0.2,1):1, u=(nFol>1? i/(nFol-1):0); const nodeX=rootX + windLean*38*u*S + (rl()*2-1)*5, nodeY=by-4 - u*axisH*0.96, spread=lerp(1.18,0.46,u); const ang=UP + side*spread + windLean*0.3 + (rl()*2-1)*0.12, len=(46+78*m)*lerp(1.15,0.5,u)*(0.78+0.34*rl())*emerge*S; lobedLeaf(nodeX,nodeY,ang,len,rl); } const boltF=smooth(0.24,0.62,m), nStemF=clamp(boltF*(0.8 + density*4.2*D.stems) + (rng('ns',seed)()*2-1)*0.4, 0, 5), nStem=Math.ceil(nStemF); function growStem(sx, hLen, nod, rs){ const steps=Math.max(3,Math.ceil(hLen/GRID)), wob=(rs()*2-1)*0.6, ph=rs()*6.28, aim=UP+windLean*0.7; let dir=UP+windLean*0.5, pos=[sx,by-4]; for(let s=1;s<=steps;s++){ const u=s/steps, exposure=clamp((by-pos[1])/520,0,1); dir += angTo(dir,aim)*0.06 + Math.sin(u*5+ph+wob*6)*0.009*D.waviness + sen*0.045*exposure; if(nod>0 && u>0.66) dir += angTo(dir,DOWN)*nod*0.17*smooth(0.66,1,u); const cd=[Math.cos(dir),Math.sin(dir)]; pos=[pos[0]+cd[0]*GRID, pos[1]+cd[1]*GRID]; C.push(pos[0],pos[1], 50, dirSym(cd[0],cd[1]), shade(stemC,rs), 'stem'); if(s%3===0 && rs()<0.5*D.bristly){ const px=-cd[1], py=cd[0], sd=rs()<0.5?1:-1; C.push(pos[0]+px*sd*GRID, pos[1]+py*sd*GRID, 48, '-', shade(leafD,rs), 'leaves'); } } return {tip:pos, dir}; } function drawBud(bcx,bcy,R,rs){ for(let gx=-R;gx<=R;gx+=GRID) for(let gy=-R*1.3;gy<=R*1.3;gy+=GRID){ if((gx*gx)/(R*R)+(gy*gy)/(R*1.3*R*1.3)>1) continue; const edge=(gx*gx)/(R*R)+(gy*gy)/(R*1.3*R*1.3)>0.62; C.push(bcx+gx,bcy+gy,72, dirSym(0.25,1), shade(FC(mix(P.bud, P.leafDark, edge?0.4:0.1)),rs),'leaves'); } for(let k=0;k<7;k++){ const a=ra()*TAU; C.push(bcx+Math.cos(a)*R*1.15*0.7, bcy+Math.sin(a)*R*1.4, 71, '/', shade(leafD,rs),'leaves'); } } function drawBloom(bcx,bcy,R,open,faceA,rs){ const Rp=R*(0.5+0.5*open), off=ra()*TAU; for(let k=0;k<4;k++){ const bk=off+k*TAU/4, ax=Math.cos(bk), ay=Math.sin(bk), px=-ay, py=ax, du=GRID/Math.max(Rp,1); for(let u=0.12;u<=1;u+=du){ const flare=Math.pow(Math.sin(Math.PI*Math.min(u,0.999)),0.72), rag=1 - D.petalRag*Math.abs(Math.sin(u*7+bk*3)), halfW=0.62*Rp*flare*rag, cxp=bcx+ax*u*Rp, cyp=bcy+ay*u*Rp; for(let w=-halfW;w<=halfW;w+=GRID){ const edge=Math.abs(w)>halfW-GRID*1.4; const col = u<0.30 ? FC(P.blotch) : (edge? FC(P.petalEdge) : (w*ax+0>0?petalC:petalWarm)); C.push(cxp+px*w, cyp+py*w, 68, dirSym(ax,ay), shade(mix(col,FC(P.stemDry),browny*0.6),rs), 'flowers'); } } } const Rc=R*0.26; for(let gx=-Rc;gx<=Rc;gx+=GRID) for(let gy=-Rc;gy<=Rc;gy+=GRID) if(Math.hypot(gx,gy)<Rc) C.push(bcx+gx,bcy+gy,90, ra()<0.4?'.':'+', shade(FC(P.center),rs),'flowers'); for(let k=0;k<13;k++){ const a=k/13*TAU; C.push(bcx+Math.cos(a)*R*0.36, bcy+Math.sin(a)*R*0.36, 91, '.', shade(FC(P.stamen),rs),'flowers'); } } function drawCapsule(bcx,bcy,R,rs){ const Hc=R*1.45, dry=Math.max(sen,browny*0.6), cc=mix(capC,FC(P.capsuleDry),dry); for(let gy=-Hc*0.5;gy<=Hc*0.55;gy+=GRID){ const u=(gy+Hc*0.5)/Hc, halfW=R*Math.sin(Math.PI*clamp(u*0.9+0.08,0,1)); for(let gx=-halfW;gx<=halfW;gx+=GRID){ const edge=Math.abs(gx)>halfW-GRID*1.2; C.push(bcx+gx,bcy+gy,86,'|', shade(edge?mix(cc,'#000000',0.18):cc,rs),'flowers'); } } for(let k=0;k<11;k++){ const a=Math.PI + (k/10-0.5)*Math.PI; C.push(bcx+Math.cos(a)*R*0.95, bcy-Hc*0.5+Math.sin(a)*R*0.95*0.32, 88, '-', shade(mix(cc,'#000000',0.22),rs),'flowers'); } for(let k=0;k<7;k++) C.push(bcx+(rs()*2-1)*R*0.7, bcy-Hc*0.36, 89, '.', shade(FC(P.pore),rs),'flowers'); } for(let i=0;i<nStem;i++){ const rs=rng('stem',seed,i), emerge=i===nStem-1?clamp(nStemF-(nStem-1),0.02,1):1, grow=smooth(0,1,emerge); let phase=clamp(0.12 + m*0.66 - i*0.12 + (rs()*2-1)*0.05, 0, 1.25); phase=clamp(phase + sen*0.6, 0, 1.3); const sx=rootX + (i-(nStem-1)/2)*D.spread*0.62*S + (rs()*2-1)*8, hLen=((axisH+50*S) + (160+300*m)*D.stature*(0.80+0.30*rs())*(0.85+0.15*v)*S)*grow; const seasonVig=0.6+0.4*seasonBloom, canBloom=rewards*(1-sen)*seasonVig; const isBud=phase<0.44, isBloom=!isBud && phase<0.74 && canBloom>0.3, nod=isBud ? clamp(1 - smooth(0.16,0.44,phase),0,1) : 0; const {tip}=growStem(sx, hLen, nod, rs), tx=tip[0], ty=tip[1]; if(isBud){ drawBud(tx,ty,(8+8*m)*emerge*(0.7+0.3*v)*S,rs); } else if(isBloom){ const open=smooth(0.44,0.62,phase)*(0.6+0.4*canBloom); drawBloom(tx,ty,(30+34*m)*emerge*(0.72+0.28*v)*(0.6+0.4*open)*S,open,UP,rs); } else { drawCapsule(tx,ty,(13+15*m)*emerge*(0.78+0.22*v)*S,rs); } } } // ===================== SETTING: Solo / Pair / Drift / Meadow / rare Field carpet ===================== const roll=rng('setting',st.seed)(); const nC = roll<0.16?0 : roll<0.44?1 : roll<0.72?2 : roll<0.91?3+Math.floor(rng('fn',st.seed)()*2) : 8+Math.floor(rng('fn',st.seed)()*6); const field=nC>=6, comps=[]; for(let i=0;i<nC;i++){ const cseed='0x'+(hashStr(st.seed+'|c'+i)>>>0).toString(16), rc=rng('comp',st.seed,i); let scale,off,raise,fade,depth; if(field){ const slot=(i+0.5)/nC, xr=clamp(slot+(rc()*2-1)*(0.6/nC),0.03,0.97); depth=0.15+0.85*rc(); scale=lerp(0.6,0.3,depth)*(0.9+0.2*rc()); raise=(depth*135+rc()*24); fade=lerp(0.42,0.70,depth); off=lerp(70,W-70,xr)-baseX; } else { const side=(i%2===0?1:-1), f=i/Math.max(1,nC); depth=f; scale=lerp(0.72,0.44,f)*(0.9+0.2*rc()); off=side*(86+i*50+rc()*46); raise=(36+i*24+rc()*32); fade=lerp(0.38,0.58,f); } comps.push({seed:cseed, artSeed:cseed, cx:baseX+off, by:baseY-raise, S:scale, fade, m, density:clamp(density+(rc()*2-1)*(field?0.18:0.12),0.04,1), v, sen, browny, rewards, depth}); } comps.sort((a,b)=>b.depth-a.depth).forEach(paintPoppy); paintPoppy({seed:st.seed, artSeed:st.artSeed, cx:baseX, by:baseY, S:1.0, fade:0, m, density, v, sen, browny, rewards}); }, traits(st,env){ const roll=rng('setting',st.seed)(); const setting = roll<0.16?'Solo' : roll<0.44?'Pair' : roll<0.72?'Drift' : roll<0.91?'Meadow':'Field'; const dd=rng('dna',st.seed); dd();dd();dd();dd();dd();dd();dd();dd(); // advance to windStr's draw (9th) const windStr=Math.pow(dd(),2.3); return [ ['Setting', setting], ['Stems', Math.max(1,Math.ceil(clamp(0.7+env.density*5.2,0.7,6.5)))], ['Wind', windStr<0.12?'Still':windStr<0.40?'Breezy':windStr<0.70?'Wind-bent':'Storm-bent'], ['Bloom', st.diedAt>0?'Capsules':(env.m>=0.58 && env.rewards>0.3?'Scarlet':'—')], ]; } })); })(); ; /* species/saguaro.js */ /* saguaro — species index 13. GENERATED from prototypes/saguaro-cactus.html by extract.js. Do not edit here — edit the prototype and re-run `npm run extract`. The prototype's mount() call is redirected to SeedlingRenderer.registerSpecies via the local shim. */ (function () { const mount = (spec) => SeedlingRenderer.registerSpecies(13, spec); /* Saguaro Cactus — Carnegiea gigantea. A single tall RIBBED COLUMN with a rounded dome top — the ribs are vertical pleats that accordion in/out with water; spines sit at areoles along the ribs. ARMS appear only after long growth (high maturity): each curves OUT then bends UP at the elbow. The reward is an apex CROWN of white funnel flowers with golden stamen centres → ruby fruit that splits to black seeds. Near-unkillable (CAM desert); drought just contracts the pleats. Death = a standing pale RIB SKELETON. Solitary, but framed by desert companions (ocotillo, prickly pear, agave). */ const SP = window.SEEDLING_SPECIES.find(x=>x.key==='saguaro'); mount(Object.assign(window.SEEDLING_toSpec(SP), { title:'Saguaro Cactus', sub:'carnegiea v2', senFrom:0.86, noSoil:true, note:'Legendary · slow ribbed column · arms only after long growth · apex crown of white flowers → red fruit · pleats accordion with drought · death = standing rib skeleton.', PAL:{ soil:'#C2A573', sand:'#D8C29A', pebble:'#B59A6E', bodyLt:'#88A56F', body:'#6E8B6E', bodyDk:'#47643F', rib:'#37502F', spine:'#E0D4AE', petal:'#FCFAF4', petalEdge:'#C2C4B2', petalShade:'#B7B4A8', stamen:'#EBC53E', stamenDk:'#C99A24', floBase:'#8A9A58', bud:'#7E9560', budTip:'#5E6B3E', fruit:'#A8332A', fruitDk:'#7E2018', fruitGreen:'#85974D', seed:'#241B14', pulp:'#C86A52', skelLt:'#C3B088', skelDk:'#8C7A5A', comp:'#9BA873', compDk:'#6E7A4E' }, stageName(st,env){ if(st.diedAt>0)return'Rib skeleton'; const m=env.m; if(m<0.12)return'Seedling'; if(m<0.5)return'Column'; if(m<0.72)return'Juvenile'; if(m<0.86)return'Armed'; return'Crowned'; }, draw(st, C, env){ const {m,v,sen,season,browny,density,rewards,reproOK,dormancy,dead,baseX,baseY}=env, P=this.PAL; const SPEC=this; // nested paintSaguaro() loses `this` const bgC = BACKDROPS[backdropFor(st.seed)]; const seasonF = (season==='Spring'||season==='Summer')?1:0.35; // ===================== one saguaro (repeated to form a random stand) ===================== function paintSaguaro(I){ const {cx, by, S, fade, m, v, sen, rewards, reproOK, browny, dead, pseed} = I; const FC = c => fade>0 ? mix(c, bgC, fade) : c; const drySkel = clamp((dead?1:0) + smooth(0.86,1.0,Math.max(sen, I.ageF||0))*0.9, 0, 1); // → standing rib skeleton const accordion = 0.78 + 0.22*v; // pleats contract when thirsty (recovers fully) const tone=(c,rs)=>shade(FC(mix(mix(c,'#b9a06a',browny*0.5), c===P.rib?P.skelDk:P.skelLt, drySkel)),rs); const COLS={lt:P.bodyLt, dk:P.bodyDk, rib:P.rib}; const isAutumn=season==='Autumn'; // Flower → fruit is sequenced purely by MATURITY (monotonic m), flicker-free: a freshly-reproductive // saguaro (m≈0.75) FLOWERS white; once well-established (m≥0.80) the same crown sets RED FRUIT. // Because m only ever RISES, the flower phase ALWAYS precedes the fruit — but fruit is NOT gated on // the in-app calendar season (a plant matured via entries, which barely advance the day, would // otherwise show flowers forever and never fruit). Autumn still ripens the fruit DEEPER red below. const crownMode = (m>=0.80) ? 'fruit' : 'flower'; const lush = clamp(rewards*(1-0.6*sen), 0, 1); // smooth amount control (no mode-flipping thresholds) const crownOn = reproOK && lush>0.10 && m>=0.75; // vanishes only under severe neglect / off-season // -- a ribbed segment: a cross-row of vertical pleats, 3-D shaded (centre lit, edges in shadow) -- function ribRow(px,py,dirX,dirY,hw,nRib,z,rs,areole){ const perpX=-dirY, perpY=dirX, gl=dirSym(dirX,dirY); for(let j=0;j<nRib;j++){ const th=-Math.PI/2 + Math.PI*(nRib>1?j/(nRib-1):0.5); const off=Math.sin(th)*hw, lit=Math.cos(th), edge=Math.abs(th)>Math.PI*0.42; const col = edge?COLS.rib : mix(COLS.dk, COLS.lt, lit*lit); C.push(px+perpX*off, py+perpY*off, z, gl, tone(col,rs),'stem'); if(areole && !edge && j%2===0 && drySkel<0.5) C.push(px+perpX*off, py+perpY*off, z+1,'+', shade(FC(mix(P.spine,bgC,0.1)),rs),'stem'); } } // -- per-seed DNA: each saguaro's silhouette is distinct (girth, height, lean + a full arm plan), // so seeds aren't all the predictable symmetric two-arm Y. Shared with traits() via armPl