You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

176 lines
5.2 KiB

export class StickFigureRenderer {
draw(player, ctx, time) {
const headRadius = 10;
const x = player.x;
let y = player.y;
const crouch = !!player.crouching;
const bodyLength = crouch ? 20 : 30;
const legLength = crouch ? 20 : 25;
if (crouch) y += 15; // shift down a touch so crouch looks grounded
// Set drawing styles
ctx.strokeStyle = "black";
ctx.fillStyle = "black";
ctx.lineWidth = 2;
// head
ctx.beginPath();
ctx.arc(x, y, headRadius, 0, Math.PI * 2);
ctx.stroke();
// eye
ctx.beginPath();
ctx.arc(x + player.facing * 5, y - 3, 2, 0, Math.PI * 2);
ctx.fill();
// body
const chestY = y + headRadius;
const hipY = y + headRadius + bodyLength;
ctx.beginPath();
ctx.moveTo(x, chestY);
ctx.lineTo(x, hipY);
ctx.stroke();
// arms
ctx.beginPath();
ctx.moveTo(x, y + 20);
if (player.action === "punch" && player.hitFrame < 10) {
ctx.lineTo(x + 30 * player.facing, y + 10);
} else {
ctx.lineTo(x + 20 * player.facing, y + 20);
}
ctx.moveTo(x, y + 20);
ctx.lineTo(x - 20 * player.facing, y + 20);
ctx.stroke();
// legs
ctx.beginPath();
if (player.state === 'ground' && player.vx !== 0) {
const step = Math.sin(time * 0.75);
const stride = 14;
const lift = 0;
// right leg
ctx.moveTo(x, hipY);
ctx.lineTo(x + stride * step, hipY + legLength + (-lift * Math.abs(step)));
// left leg
ctx.moveTo(x, hipY);
ctx.lineTo(x - stride * step, hipY + legLength + (-lift * Math.abs(step)));
} else {
const idleSpread = 10;
ctx.moveTo(x, hipY);
ctx.lineTo(x + idleSpread, hipY + legLength);
ctx.moveTo(x, hipY);
ctx.lineTo(x - idleSpread, hipY + legLength);
}
ctx.stroke();
ctx.strokeStyle = "black";
// speech bubble
if (player.talking) {
const padding = 0;
const maxMessageWidth = 100;
// set font BEFORE measuring
ctx.font = "12px sans-serif";
const lines = this._wrapText(ctx, player.message, maxMessageWidth);
const textWidth = ctx.measureText(player.message).width;
const widths = lines.map(line => ctx.measureText(line).width);
const contentWidth = Math.max(...widths, textWidth);
const bubbleWidth = Math.min(contentWidth + padding + 10, maxMessageWidth + padding + 10);
const lineHeight = 14;
const bubbleHeight = lines.length * lineHeight + padding;
const bubbleX = x - bubbleWidth - 15;
const bubbleY = y - 20;
ctx.beginPath();
ctx.fillStyle = "white";
ctx.strokeStyle = "black";
if (typeof ctx.roundRect === "function") {
ctx.roundRect(bubbleX, bubbleY, bubbleWidth, bubbleHeight, 4);
} else {
ctx.rect(bubbleX, bubbleY, bubbleWidth, bubbleHeight);
}
ctx.fill();
ctx.stroke();
ctx.fillStyle = "black";
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], bubbleX + padding, bubbleY + padding + i * lineHeight);
}
}
let nameY = y - 80;
if (player.isAlive) {
nameY = this._drawHealthBar(ctx, player)
}
// name tag
ctx.save();
ctx.font = "10px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
ctx.fillStyle = "black";
ctx.fillText(player.id, x, nameY);
ctx.restore();
}
_drawHealthBar(ctx, player) {
const x = player.x;
const headCenterY = player.y;
const topOfHead = headCenterY - 10;
const barWidth = 40;
const barHeight = 5;
const barX = x - barWidth / 2;
const barY = topOfHead - 10;
ctx.fillStyle = "#ddd";
ctx.fillRect(barX, barY, barWidth, barHeight);
const pct = Math.max(0, Math.min(1, player.hp / player.maxHp));
let color = '#27ae60';
if (pct <= 0.33) {
color = '#e74c3c';
} else if (pct <= 0.66) {
color = '#f1c40f';
}
ctx.fillStyle = color;
ctx.fillRect(barX, barY, barWidth * pct, barHeight);
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.strokeRect(barX, barY, barWidth, barHeight);
return barY;
}
_wrapText(ctx, text, maxWidth) {
const words = text.split(" ");
const lines = [];
let line = "";
for (let i = 0; i < words.length; i++) {
const testLine = line + words[i] + " ";
const testWidth = ctx.measureText(testLine).width;
if (testWidth > maxWidth && i > 0) {
lines.push(line.trimEnd());
line = words[i] + " ";
} else {
line = testLine;
}
}
lines.push(line.trim());
return lines;
}
}