200 lines
10 KiB
Java
200 lines
10 KiB
Java
package com.projectsacrifice.game;
|
|
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Path;
|
|
|
|
/** Canvas-only adapter for the normalized volunteer skeleton. */
|
|
final class VolunteerCanvasRenderer {
|
|
private static final int FRAME_COUNT = 16;
|
|
private static final VolunteerRig.Pose[][][][] POSES = buildPoseCache();
|
|
|
|
void draw(Canvas canvas, Paint fill, Paint outline, Path path,
|
|
float x, float y, float width, float height,
|
|
VolunteerRig.Skin skin, VolunteerRig.Motion motion,
|
|
float phase, int facing, float alpha, boolean highContrast) {
|
|
VolunteerRig.Pose pose = cachedPose(skin, motion, phase, facing);
|
|
int frameColor = highContrast ? Color.WHITE : pose.palette().frame();
|
|
int shellColor = highContrast ? Color.rgb(255, 214, 77) : pose.palette().shell();
|
|
int clothColor = highContrast ? Color.WHITE : pose.palette().cloth();
|
|
int glowColor = highContrast ? Color.BLACK : pose.palette().glow();
|
|
|
|
outline.setStyle(Paint.Style.STROKE);
|
|
outline.setStrokeCap(Paint.Cap.ROUND);
|
|
outline.setStrokeWidth(Math.max(3f, width * 0.075f));
|
|
outline.setColor(withAlpha(frameColor, alpha));
|
|
|
|
line(canvas, outline, pose, VolunteerRig.Joint.HIPS, VolunteerRig.Joint.CHEST,
|
|
x, y, width, height);
|
|
line(canvas, outline, pose, VolunteerRig.Joint.LEFT_SHOULDER,
|
|
VolunteerRig.Joint.LEFT_HAND, x, y, width, height);
|
|
line(canvas, outline, pose, VolunteerRig.Joint.RIGHT_SHOULDER,
|
|
VolunteerRig.Joint.RIGHT_HAND, x, y, width, height);
|
|
line(canvas, outline, pose, VolunteerRig.Joint.HIPS,
|
|
VolunteerRig.Joint.LEFT_KNEE, x, y, width, height);
|
|
line(canvas, outline, pose, VolunteerRig.Joint.LEFT_KNEE,
|
|
VolunteerRig.Joint.LEFT_FOOT, x, y, width, height);
|
|
line(canvas, outline, pose, VolunteerRig.Joint.HIPS,
|
|
VolunteerRig.Joint.RIGHT_KNEE, x, y, width, height);
|
|
line(canvas, outline, pose, VolunteerRig.Joint.RIGHT_KNEE,
|
|
VolunteerRig.Joint.RIGHT_FOOT, x, y, width, height);
|
|
|
|
float chestX = px(pose, VolunteerRig.Joint.CHEST, x, width);
|
|
float chestY = py(pose, VolunteerRig.Joint.CHEST, y, height);
|
|
float hipsX = px(pose, VolunteerRig.Joint.HIPS, x, width);
|
|
float hipsY = py(pose, VolunteerRig.Joint.HIPS, y, height);
|
|
float headX = px(pose, VolunteerRig.Joint.HEAD, x, width);
|
|
float headY = py(pose, VolunteerRig.Joint.HEAD, y, height);
|
|
|
|
fill.setColor(withAlpha(clothColor, alpha * 0.86f));
|
|
path.reset();
|
|
path.moveTo(chestX - width * 0.22f * facing, chestY + height * 0.04f);
|
|
path.lineTo(hipsX - width * 0.36f * facing, hipsY + height * 0.17f);
|
|
path.lineTo(hipsX, hipsY + height * 0.06f);
|
|
path.close();
|
|
canvas.drawPath(path, fill);
|
|
|
|
fill.setColor(withAlpha(shellColor, alpha));
|
|
canvas.drawRoundRect(chestX - width * 0.22f, chestY - height * 0.08f,
|
|
chestX + width * 0.22f, hipsY + height * 0.04f,
|
|
width * 0.13f, width * 0.13f, fill);
|
|
canvas.drawCircle(headX, headY, width * 0.22f, fill);
|
|
|
|
outline.setStrokeWidth(Math.max(2f, width * 0.045f));
|
|
canvas.drawCircle(headX, headY, width * 0.22f, outline);
|
|
canvas.drawRoundRect(chestX - width * 0.22f, chestY - height * 0.08f,
|
|
chestX + width * 0.22f, hipsY + height * 0.04f,
|
|
width * 0.13f, width * 0.13f, outline);
|
|
|
|
fill.setColor(withAlpha(pose.visorLit() ? glowColor : frameColor,
|
|
pose.visorLit() ? alpha : alpha * 0.72f));
|
|
canvas.drawRoundRect(headX - width * 0.14f, headY - height * 0.025f,
|
|
headX + width * 0.14f, headY + height * 0.025f,
|
|
width * 0.025f, width * 0.025f, fill);
|
|
|
|
drawJoint(canvas, fill, pose, VolunteerRig.Joint.LEFT_SHOULDER,
|
|
x, y, width, height, frameColor, alpha);
|
|
drawJoint(canvas, fill, pose, VolunteerRig.Joint.RIGHT_SHOULDER,
|
|
x, y, width, height, frameColor, alpha);
|
|
drawJoint(canvas, fill, pose, VolunteerRig.Joint.LEFT_KNEE,
|
|
x, y, width, height, frameColor, alpha);
|
|
drawJoint(canvas, fill, pose, VolunteerRig.Joint.RIGHT_KNEE,
|
|
x, y, width, height, frameColor, alpha);
|
|
|
|
if (pose.anchorBolt()) {
|
|
fill.setColor(withAlpha(glowColor, alpha));
|
|
canvas.drawCircle(hipsX, hipsY, width * 0.14f, fill);
|
|
outline.setColor(withAlpha(frameColor, alpha));
|
|
canvas.drawLine(hipsX - width * 0.1f, hipsY, hipsX + width * 0.1f, hipsY, outline);
|
|
canvas.drawLine(hipsX, hipsY - width * 0.1f, hipsX, hipsY + width * 0.1f, outline);
|
|
}
|
|
if (pose.frozenShell()) drawFrozenShell(canvas, fill, outline, x, y, width, height, alpha);
|
|
if (pose.conductiveArcs()) drawConductiveArcs(canvas, outline, path,
|
|
x, y, width, height, phase, alpha);
|
|
if (motion == VolunteerRig.Motion.RESPAWN) drawAssemblyRings(canvas, outline,
|
|
x, y, width, height, pose.assemblyProgress(), alpha);
|
|
}
|
|
|
|
private static void drawFrozenShell(Canvas canvas, Paint fill, Paint outline,
|
|
float x, float y, float width, float height, float alpha) {
|
|
fill.setColor(withAlpha(Color.rgb(85, 205, 236), alpha * 0.34f));
|
|
canvas.drawRoundRect(x - width * 0.12f, y + height * 0.02f,
|
|
x + width * 1.12f, y + height,
|
|
width * 0.12f, width * 0.12f, fill);
|
|
outline.setColor(withAlpha(Color.rgb(218, 252, 255), alpha));
|
|
outline.setStrokeWidth(Math.max(2f, width * 0.045f));
|
|
canvas.drawLine(x, y + height * 0.18f, x + width * 0.28f, y + height * 0.45f, outline);
|
|
canvas.drawLine(x + width, y + height * 0.16f,
|
|
x + width * 0.72f, y + height * 0.52f, outline);
|
|
}
|
|
|
|
private static void drawConductiveArcs(Canvas canvas, Paint outline, Path path,
|
|
float x, float y, float width, float height,
|
|
float phase, float alpha) {
|
|
outline.setColor(withAlpha(Color.rgb(255, 239, 74), alpha));
|
|
outline.setStrokeWidth(Math.max(3f, width * 0.055f));
|
|
float pulse = ((int) (phase * 12f) & 1) == 0 ? 0f : width * 0.06f;
|
|
path.reset();
|
|
path.moveTo(x - width * 0.15f, y + height * 0.3f);
|
|
path.lineTo(x + width * 0.08f, y + height * 0.22f + pulse);
|
|
path.lineTo(x - width * 0.02f, y + height * 0.52f);
|
|
path.lineTo(x + width * 0.18f, y + height * 0.68f - pulse);
|
|
canvas.drawPath(path, outline);
|
|
path.reset();
|
|
path.moveTo(x + width * 1.15f, y + height * 0.28f);
|
|
path.lineTo(x + width * 0.91f, y + height * 0.43f - pulse);
|
|
path.lineTo(x + width * 1.03f, y + height * 0.66f);
|
|
path.lineTo(x + width * 0.82f, y + height * 0.77f + pulse);
|
|
canvas.drawPath(path, outline);
|
|
}
|
|
|
|
private static void drawAssemblyRings(Canvas canvas, Paint outline,
|
|
float x, float y, float width, float height,
|
|
float progress, float alpha) {
|
|
outline.setColor(withAlpha(Color.rgb(116, 232, 213), alpha * (1f - progress * 0.3f)));
|
|
outline.setStrokeWidth(Math.max(2f, width * 0.045f));
|
|
float cx = x + width * 0.5f;
|
|
float cy = y + height * 0.55f;
|
|
canvas.drawCircle(cx, cy, width * (0.25f + progress * 0.45f), outline);
|
|
canvas.drawCircle(cx, cy, width * (0.48f + progress * 0.35f), outline);
|
|
}
|
|
|
|
private static void drawJoint(Canvas canvas, Paint fill, VolunteerRig.Pose pose,
|
|
VolunteerRig.Joint joint, float x, float y,
|
|
float width, float height, int color, float alpha) {
|
|
fill.setColor(withAlpha(color, alpha));
|
|
canvas.drawCircle(px(pose, joint, x, width), py(pose, joint, y, height),
|
|
Math.max(2.5f, width * 0.065f), fill);
|
|
}
|
|
|
|
private static void line(Canvas canvas, Paint outline, VolunteerRig.Pose pose,
|
|
VolunteerRig.Joint from, VolunteerRig.Joint to,
|
|
float x, float y, float width, float height) {
|
|
canvas.drawLine(px(pose, from, x, width), py(pose, from, y, height),
|
|
px(pose, to, x, width), py(pose, to, y, height), outline);
|
|
}
|
|
|
|
private static float px(VolunteerRig.Pose pose, VolunteerRig.Joint joint,
|
|
float x, float width) {
|
|
return x + pose.x(joint) * width;
|
|
}
|
|
|
|
private static float py(VolunteerRig.Pose pose, VolunteerRig.Joint joint,
|
|
float y, float height) {
|
|
return y + pose.y(joint) * height;
|
|
}
|
|
|
|
private static VolunteerRig.Pose cachedPose(VolunteerRig.Skin skin,
|
|
VolunteerRig.Motion motion,
|
|
float phase, int facing) {
|
|
int frame = Math.min(FRAME_COUNT - 1,
|
|
Math.max(0, (int) ((phase - Math.floor(phase)) * FRAME_COUNT)));
|
|
return POSES[skin.ordinal()][motion.ordinal()][facing < 0 ? 0 : 1][frame];
|
|
}
|
|
|
|
private static VolunteerRig.Pose[][][][] buildPoseCache() {
|
|
VolunteerRig.Skin[] skins = VolunteerRig.Skin.values();
|
|
VolunteerRig.Motion[] motions = VolunteerRig.Motion.values();
|
|
VolunteerRig.Pose[][][][] result =
|
|
new VolunteerRig.Pose[skins.length][motions.length][2][FRAME_COUNT];
|
|
for (VolunteerRig.Skin skin : skins) {
|
|
for (VolunteerRig.Motion motion : motions) {
|
|
for (int facingIndex = 0; facingIndex < 2; facingIndex++) {
|
|
for (int frame = 0; frame < FRAME_COUNT; frame++) {
|
|
result[skin.ordinal()][motion.ordinal()][facingIndex][frame] =
|
|
VolunteerRig.pose(skin, motion, frame / (float) (FRAME_COUNT - 1),
|
|
facingIndex == 0 ? -1 : 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static int withAlpha(int color, float alpha) {
|
|
int clamped = Math.max(0, Math.min(255, Math.round(Color.alpha(color) * alpha)));
|
|
return Color.argb(clamped, Color.red(color), Color.green(color), Color.blue(color));
|
|
}
|
|
}
|