improve gear tinting and unlock render tests

This commit is contained in:
jwinterm
2026-07-06 04:38:28 -04:00
parent 83020f6c08
commit c89c01e5f1
5 changed files with 65 additions and 13 deletions
+1
View File
@@ -96,6 +96,7 @@
var p = RK.pack(id);
if (!p) return false;
if (!p.premium) return true;
if (RK.renderModeTestUnlocks && RK.renderModeTestUnlocks() && (p.projection === 'iso' || p.projection === '3d')) return true;
return !!(RK.entitlements && RK.entitlements.packs && RK.entitlements.packs[id]);
};
RK.canUseAppearance = function (a) {
+12 -3
View File
@@ -177,8 +177,9 @@
ctx.beginPath(); ctx.moveTo(17, 26); ctx.lineTo(35, 26); ctx.stroke();
return;
}
var tint = item.tint === false ? 'none' : ((draft.colors && draft.colors[slot]) || draft.tint);
var colors = item.tint === false ? null : draft.colors;
var colorable = item.colorable !== false;
var tint = colorable ? ((draft.colors && draft.colors[slot]) || draft.tint) : 'none';
var colors = colorable ? draft.colors : null;
if (!RK.drawCharTileCanvas || !RK.drawCharTileCanvas(ctx, item.frame, tint, 2, 2, 48, 48, colors, slot)) {
RK.loadCharAtlas(draw);
ctx.fillStyle = '#666'; ctx.font = '11px monospace'; ctx.textAlign = 'center'; ctx.fillText('...', 26, 30);
@@ -416,6 +417,7 @@
function modeStatus(mode, unlocked) {
if (!mode || !mode.premium) return 'Included';
if (unlocked && RK.renderModeTestUnlocks && RK.renderModeTestUnlocks()) return 'Test';
return unlocked ? 'Unlocked' : 'Locked';
}
@@ -479,6 +481,10 @@
}
}
function equipmentColorable(item) {
return !!(item && item.frame != null && item.colorable !== false);
}
function renderEditor() {
editSection.innerHTML = '';
if (!isCharDraft()) return;
@@ -493,6 +499,7 @@
(RK.CHAR_EQUIPMENT_SLOTS || []).forEach(function (slot) {
var catalog = RK.CHAR_EQUIPMENT[slot] || {};
var opts = row(slot.charAt(0).toUpperCase() + slot.slice(1), editSection);
var selectedItem = catalog[draft.equipment[slot]];
for (var id in catalog) {
(function (item) {
opts.appendChild(optionButton(item.label, draft.equipment[slot] === item.id, function () {
@@ -502,7 +509,9 @@
}, optionTileCanvas(slot, item, draft, null)));
})(catalog[id]);
}
renderColorRow(slot.charAt(0).toUpperCase() + slot.slice(1) + ' color', slot, 'tint', editSection);
if (equipmentColorable(selectedItem)) {
renderColorRow(slot.charAt(0).toUpperCase() + slot.slice(1) + ' color', slot, 'tint', editSection);
}
});
}
+29 -10
View File
@@ -33,6 +33,21 @@
'192,117,28': 2,
'198,101,39': 3
};
var GEAR_SOURCE = {
'114,88,57': 0,
'149,92,24': 1,
'163,84,34': 1,
'176,128,82': 1,
'177,92,35': 2,
'192,117,28': 2,
'198,101,39': 3,
'200,164,128': 2,
'135,135,135': 0,
'202,202,202': 2,
'204,204,204': 2,
'232,232,232': 3,
'247,247,247': 3
};
RK.CHAR_TINTS = {
none: { id: 'none', label: 'Natural', color: null, ramp: null },
@@ -80,9 +95,9 @@
},
head: {
none: { id: 'none', label: 'None', frame: null },
hood: { id: 'hood', label: 'Hood', frame: 24, tint: false },
helm: { id: 'helm', label: 'Helm', frame: 454, tint: false },
horns: { id: 'horns', label: 'Horns', frame: 354, tint: false },
hood: { id: 'hood', label: 'Hood', frame: 24 },
helm: { id: 'helm', label: 'Helm', frame: 454 },
horns: { id: 'horns', label: 'Horns', frame: 354 },
cap: { id: 'cap', label: 'Cap', frame: 462 }
},
shield: {
@@ -90,14 +105,14 @@
round: { id: 'round', label: 'Round', frame: 254 },
kite: { id: 'kite', label: 'Kite', frame: 304 },
tower: { id: 'tower', label: 'Tower', frame: 202 },
buckler:{ id: 'buckler', label: 'Buckler', frame: 411, tint: false }
buckler:{ id: 'buckler', label: 'Buckler', frame: 411 }
},
weapon: {
none: { id: 'none', label: 'None', frame: null },
staff: { id: 'staff', label: 'Staff', frame: 154 },
sword: { id: 'sword', label: 'Sword', frame: 369, tint: false },
axe: { id: 'axe', label: 'Axe', frame: 375, tint: false },
bow: { id: 'bow', label: 'Bow', frame: 107, tint: false }
sword: { id: 'sword', label: 'Sword', frame: 369 },
axe: { id: 'axe', label: 'Axe', frame: 375 },
bow: { id: 'bow', label: 'Bow', frame: 107 }
}
};
RK.CHAR_EQUIPMENT_SLOTS = EQUIP_SLOTS.slice();
@@ -234,7 +249,8 @@
var skin = RK.CHAR_SKIN_TONES[colors.skin];
var hair = RK.CHAR_HAIR_COLORS[colors.hair];
var slotTint = RK.CHAR_TINTS[slot === 'base' ? colors.base : (colors[slot] || tintId)];
var needsSlotTint = slotTint && slotTint.ramp && (slot !== 'base' || colors.base !== 'none');
var slotRamp = slotTint && slotTint.ramp ? slotTint.ramp.map(hexToRgb) : null;
var needsSlotTint = !!slotRamp && (slot !== 'base' || colors.base !== 'none');
var needsBodyPalette = slot === 'base' && ((skin && colors.skin !== 'natural') || (hair && colors.hair !== 'copper') || needsSlotTint);
if (!a || (!needsBodyPalette && !needsSlotTint)) return null;
var key = idx + '|' + slot + '|' + JSON.stringify(colors);
@@ -250,8 +266,10 @@
if (slot === 'base') {
applyRamp(img, SKIN_SOURCE, skin && colors.skin !== 'natural' ? skin.ramp.map(hexToRgb) : null);
applyRamp(img, HAIR_SOURCE, hair && colors.hair !== 'copper' ? hair.ramp.map(hexToRgb) : null);
} else {
applyRamp(img, GEAR_SOURCE, slotRamp);
}
applyRamp(img, TINT_SOURCE, slotTint && slotTint.ramp ? slotTint.ramp.map(hexToRgb) : null);
applyRamp(img, TINT_SOURCE, slotRamp);
ctx.putImageData(img, 0, 0);
tintedTiles[key] = cv;
return cv;
@@ -295,7 +313,8 @@
EQUIP_SLOTS.forEach(function (slot) {
var item = RK.CHAR_EQUIPMENT[slot][eq[slot]];
if (!item || item.frame == null) return;
out.push({ slot: slot, id: item.id, frame: item.frame, tint: item.tint === false ? 'none' : tint, colorable: item.tint !== false });
var colorable = item.colorable !== false;
out.push({ slot: slot, id: item.id, frame: item.frame, tint: colorable ? tint : 'none', colorable: colorable });
});
return out;
};
+5
View File
@@ -16,6 +16,10 @@
];
RK.entitlements = RK.entitlements || { premium: false, level: 'free', packs: {} };
// Temporary production QA switch while Iso/3D/Fancy are being proven out. Flip this back to
// false when render modes return behind credits/pack entitlements.
RK.RENDER_MODE_TEST_UNLOCKS = true;
RK.renderModeTestUnlocks = function () { return RK.RENDER_MODE_TEST_UNLOCKS === true; };
RK.modeMeta = function (id) {
for (var i = 0; i < RK.RENDER_MODES.length; i++) {
@@ -27,6 +31,7 @@
RK.canUseMode = function (id) {
var m = RK.modeMeta(id);
if (!m) return false;
if (RK.renderModeTestUnlocks && RK.renderModeTestUnlocks()) return true;
if (m.pack && RK.canUsePack) return RK.canUsePack(m.pack);
return !m.premium || !!RK.entitlements.premium;
};
+18
View File
@@ -53,6 +53,7 @@ function loadRenderKit() {
fs.readFileSync(path.join(__dirname, '../html/js/render/renderModes.js'), 'utf8'),
context
);
RK.RENDER_MODE_TEST_UNLOCKS = false;
return RK;
}
@@ -98,6 +99,23 @@ describe('render pack visual resolver', () => {
expect(RK.createRenderer('iso', {}, {}).name).toBe('iso');
});
test('temporary render test unlocks bypass mode gates only for render packs', () => {
const RK = loadRenderKit();
RK.setEntitlementSnapshot({ totalCreditsPurchased: 0, premium: false, packs: {} });
expect(RK.canUseMode('iso')).toBe(false);
expect(RK.canUsePack('iso-dungeon')).toBe(false);
RK.RENDER_MODE_TEST_UNLOCKS = true;
expect(RK.canUseMode('ascii')).toBe(true);
expect(RK.canUseMode('fancy')).toBe(true);
expect(RK.canUseMode('iso')).toBe(true);
expect(RK.canUseMode('3d')).toBe(true);
expect(RK.canUsePack('iso-dungeon')).toBe(true);
expect(RK.canUsePack('kenney-3d-characters')).toBe(true);
expect(RK.canUsePack('generated-skins')).toBe(false);
});
test('render pack code stays out of payment and account APIs', () => {
const files = [
'../html/js/render/assetPacks.js',