feat: add production campaigns, gameplay labs, and review documentation

This commit is contained in:
cabinw
2026-09-13 12:27:10 +08:00
parent d1dffa9338
commit 4644e975b0
214 changed files with 98994 additions and 134 deletions
+43
View File
@@ -0,0 +1,43 @@
import { roundedHex } from './tile-shape.js';
export function tint(hex,amount){return '#'+hex.slice(1).match(/../g).map(v=>Math.round(amount>=0?parseInt(v,16)+(255-parseInt(v,16))*amount:parseInt(v,16)*(1+amount)).toString(16).padStart(2,'0')).join('');}
export function drawStyledToken(p,x,y,r,tile,angle){
const fill=tile.fill,ink='#263A3A';
if(!fill||!tile.artStyle||tile.artStyle==='classic')return false;
const face=roundedHex(x,y,r*.94),closed=[...face,face[0]];
let arrowY=y,arrowScale=.58;
if(tile.artStyle==='vivid'){
p.polygon(roundedHex(x+r*.04,y+r*.07,r*.94),'#30264C');p.polygon(face,fill);p.line(closed,'#30264C',r*.065);
p.line([{x:x-r*.45,y:y-r*.49},{x:x-r*.18,y:y-r*.64}],'#FFFFFF',r*.055);
}else if(tile.artStyle==='toy'){
p.polygon(roundedHex(x,y+r*.09,r*.94),tint(fill,-.3));
p.polygon(face,tint(fill,-.08));p.polygon(roundedHex(x,y-r*.06,r*.83),fill);
const rim=roundedHex(x,y-r*.06,r*.75);p.line(rim.slice(9,18),tint(fill,.5),r*.045);
p.circle(x,y-r*.04,r*.50,tint(fill,.11));
}else{
p.polygon(face,'#FFFCED');p.line(closed,'#526F5744',r*.035);
const species=tile.color%4,earY=y-r*.40;
const oval=(cx,cy,rx,ry)=>Array.from({length:24},(_,i)=>({x:cx+Math.cos(i*Math.PI/12)*rx,y:cy+Math.sin(i*Math.PI/12)*ry}));
if(species===0){
p.polygon([{x:x-r*.58,y:y-r*.1},{x:x-r*.53,y:y-r*.73},{x:x-r*.08,y:y-r*.4}],fill);
p.polygon([{x:x+r*.58,y:y-r*.1},{x:x+r*.53,y:y-r*.73},{x:x+r*.08,y:y-r*.4}],fill);
}else if(species===2){
for(const sign of [-1,1]){p.polygon(oval(x+sign*r*.24,y-r*.48,r*.13,r*.32),fill);p.polygon(oval(x+sign*r*.24,y-r*.53,r*.055,r*.19),tint(fill,.55));}
}else if(species===1){
for(const sign of [-1,1]){p.circle(x+sign*r*.40,earY,r*.23,fill);p.circle(x+sign*r*.40,earY,r*.12,tint(fill,.4));}
}else{
for(const sign of [-1,1])p.circle(x+sign*r*.26,y-r*.43,r*.19,fill);
}
p.circle(x,y-r*.04,r*.51,fill);
for(const sign of [-1,1]){
const eyeY=species===3?y-r*.42:y-r*.19;
if(species===3)p.circle(x+sign*r*.21,eyeY,r*.10,'#FFFFFF');
p.circle(x+sign*r*.21,eyeY,r*.058,ink);p.circle(x+sign*r*.32,y+r*.02,r*.078,'#EF958B');
}
p.line([{x:x-r*.085,y:y-r*.035},{x,y:y+r*.025},{x:x+r*.085,y:y-r*.035}],ink,r*.035);
p.circle(x,y+r*.43,r*.36,'#FFFFFF');arrowY=y+r*.43;arrowScale=.44;
}
const a=r*arrowScale;
const shape=[[-.72,-.23],[.06,-.23],[.06,-.59],[.83,0],[.06,.59],[.06,.23],[-.72,.23]];
p.polygon(shape.map(([u,v])=>({x:x+a*(u*Math.cos(angle)-v*Math.sin(angle)),y:arrowY+a*(u*Math.sin(angle)+v*Math.cos(angle))})),tile.artStyle==='animals'?ink:tile.arrow);
return true;
}