Update game UI, add 3D gear assets, backgrounds, icons, and audio enhancements
This commit is contained in:
+149
-91
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* PPE SAFETY CHALLENGE - DYNAMIC 2D WORKER AVATAR SYSTEM
|
||||
* Uses the step-by-step transparent character images from ppe_character_steps_transparent_png!
|
||||
* Dynamically switches character states as safety gear is equipped.
|
||||
* PPE SAFETY CHALLENGE - REALISTIC 3D WORKER AVATAR SYSTEM
|
||||
* Uses the exact 3D realistic character image provided by the user (images/worker_base.png),
|
||||
* with precision anatomical PPE overlays for both Grinding and Chemical scenarios.
|
||||
*/
|
||||
|
||||
class WorkerAvatar {
|
||||
@@ -18,7 +18,7 @@ class WorkerAvatar {
|
||||
|
||||
equip(itemId) {
|
||||
this.equippedItems.add(itemId);
|
||||
this.updateLayers();
|
||||
this.updateLayers(itemId);
|
||||
}
|
||||
|
||||
unequip(itemId) {
|
||||
@@ -40,107 +40,165 @@ class WorkerAvatar {
|
||||
x: relX,
|
||||
y: relY,
|
||||
eyeX: relX,
|
||||
eyeY: relY - 130,
|
||||
handX: relX + 55,
|
||||
handY: relY + 30
|
||||
eyeY: relY - 135,
|
||||
handX: relX + 65,
|
||||
handY: relY + 25
|
||||
};
|
||||
}
|
||||
|
||||
getStepImagePath() {
|
||||
const hasHelmet = this.equippedItems.has('safety_helmet_grind') || this.equippedItems.has('safety_helmet_chem');
|
||||
const hasGoggles = this.equippedItems.has('chem_splash_goggles') || this.equippedItems.has('safety_goggles_grind');
|
||||
const hasFaceShield = this.equippedItems.has('face_shield_grind') || this.equippedItems.has('face_shield_chem');
|
||||
const hasHearing = this.equippedItems.has('hearing_protection_grind') || this.equippedItems.has('hearing_protection_chem');
|
||||
const hasLeatherGloves = this.equippedItems.has('leather_grinding_gloves');
|
||||
const hasWorkGloves = this.equippedItems.has('work_gloves_grind') || this.equippedItems.has('standard_work_gloves');
|
||||
const hasChemGloves = this.equippedItems.has('chem_resistant_gloves');
|
||||
const hasVest = this.equippedItems.has('reflective_vest_grind');
|
||||
const hasApron = this.equippedItems.has('chem_resistant_apron');
|
||||
const hasCottonCoat = this.equippedItems.has('cotton_coat_chem');
|
||||
const hasBoots = this.equippedItems.has('steel_toe_boots_grind') || this.equippedItems.has('chem_safety_shoes');
|
||||
const hasMask = this.equippedItems.has('dust_mask_grind');
|
||||
|
||||
// 10. Full PPE with Mask
|
||||
if (hasMask) {
|
||||
return 'images/steps/10_full_ppe_with_mask.png';
|
||||
}
|
||||
|
||||
// 9. Full Grinding Set with Boots
|
||||
if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && (hasLeatherGloves || hasChemGloves) && (hasVest || hasApron || hasBoots)) {
|
||||
return 'images/steps/09_helmet_goggles_hearing_leather_gloves_vest_boots.png';
|
||||
}
|
||||
|
||||
// 8. With Vest / Body Wear
|
||||
if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && (hasLeatherGloves || hasChemGloves) && (hasVest || hasApron || hasCottonCoat)) {
|
||||
return 'images/steps/08_helmet_goggles_hearing_leather_gloves_vest.png';
|
||||
}
|
||||
|
||||
// 7. With Leather / Chem Gloves
|
||||
if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && (hasLeatherGloves || hasChemGloves)) {
|
||||
return 'images/steps/07_helmet_goggles_hearing_leather_gloves.png';
|
||||
}
|
||||
|
||||
// 6. With Work Gloves
|
||||
if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && hasWorkGloves) {
|
||||
return 'images/steps/06_helmet_goggles_hearing_work_gloves.png';
|
||||
}
|
||||
|
||||
// 5. With Earmuffs / Hearing Protection
|
||||
if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing) {
|
||||
return 'images/steps/05_helmet_goggles_hearing.png';
|
||||
}
|
||||
|
||||
// 4. With Face Shield
|
||||
if (hasFaceShield || (hasHelmet && hasFaceShield)) {
|
||||
return 'images/steps/04_helmet_goggles_face_shield.png';
|
||||
}
|
||||
|
||||
// 3. With Goggles & Helmet
|
||||
if (hasHelmet && hasGoggles) {
|
||||
return 'images/steps/03_helmet_goggles.png';
|
||||
}
|
||||
|
||||
// 2. Helmet Only
|
||||
if (hasHelmet) {
|
||||
return 'images/steps/02_helmet.png';
|
||||
}
|
||||
|
||||
// Individual items
|
||||
if (hasFaceShield) return 'images/steps/04_helmet_goggles_face_shield.png';
|
||||
if (hasGoggles) return 'images/steps/03_helmet_goggles.png';
|
||||
if (hasHearing) return 'images/steps/05_helmet_goggles_hearing.png';
|
||||
if (hasLeatherGloves) return 'images/steps/07_helmet_goggles_hearing_leather_gloves.png';
|
||||
if (hasWorkGloves) return 'images/steps/06_helmet_goggles_hearing_work_gloves.png';
|
||||
|
||||
// 1. Base Worker (No PPE)
|
||||
return 'images/steps/01_no_ppe.png';
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.container) return;
|
||||
|
||||
this.container.innerHTML = `
|
||||
<div class="avatar-step-container" id="avatar-step-container">
|
||||
<!-- Floor Contact Shadow -->
|
||||
<div class="avatar-floor-shadow"></div>
|
||||
|
||||
<!-- Dynamic Character Step Image -->
|
||||
<img src="images/steps/01_no_ppe.png" alt="Worker Character" class="avatar-step-img" id="avatar-step-img" />
|
||||
</div>
|
||||
<svg viewBox="0 0 682 1024" class="worker-svg" id="worker-avatar-svg" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="gearShadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="5" flood-color="#000000" flood-opacity="0.6"/>
|
||||
</filter>
|
||||
<radialGradient id="softFloorShadowGrad" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#000000" stop-opacity="0.65"/>
|
||||
<stop offset="35%" stop-color="#000000" stop-opacity="0.4"/>
|
||||
<stop offset="65%" stop-color="#000000" stop-opacity="0.18"/>
|
||||
<stop offset="85%" stop-color="#000000" stop-opacity="0.05"/>
|
||||
<stop offset="100%" stop-color="#000000" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="footContactShadowGrad" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#000000" stop-opacity="0.75"/>
|
||||
<stop offset="40%" stop-color="#000000" stop-opacity="0.45"/>
|
||||
<stop offset="75%" stop-color="#000000" stop-opacity="0.12"/>
|
||||
<stop offset="100%" stop-color="#000000" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<filter id="softFloorBlur" x="-30%" y="-30%" width="160%" height="160%">
|
||||
<feGaussianBlur stdDeviation="5"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Realistic Soft Floor Contact & Ambient Occlusion Shadow -->
|
||||
<g id="layer-floor-shadow" filter="url(#softFloorBlur)">
|
||||
<!-- Broad Ambient Floor Shadow -->
|
||||
<ellipse cx="347" cy="1008" rx="205" ry="22" fill="url(#softFloorShadowGrad)"/>
|
||||
<!-- Left Foot Direct Contact Shadow -->
|
||||
<ellipse cx="238" cy="1008" rx="72" ry="14" fill="url(#footContactShadowGrad)"/>
|
||||
<!-- Right Foot Direct Contact Shadow -->
|
||||
<ellipse cx="452" cy="1008" rx="72" ry="14" fill="url(#footContactShadowGrad)"/>
|
||||
</g>
|
||||
|
||||
<!-- ====================================================
|
||||
LAYER 1: REALISTIC 3D WORKER BASE CHARACTER IMAGE
|
||||
==================================================== -->
|
||||
<g id="layer-base-character">
|
||||
<image href="images/worker_base.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- ====================================================
|
||||
LAYER 2: MODULAR PPE EQUIPMENT (TOGGLED DYNAMICALLY)
|
||||
==================================================== -->
|
||||
|
||||
<!-- 1. FEET: Safety Shoes (Steel-Toe Boots covering bare feet) -->
|
||||
<g id="layer-gear-steel_toe_boots_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_safety_boots.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-chem_safety_shoes" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_safety_boots.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- 2. HANDS: Leather Grinding / Heavy Work Gloves & Chemical Gloves -->
|
||||
<g id="layer-gear-leather_grinding_gloves" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_leather_gloves.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-work_gloves_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_work_gloves.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-standard_work_gloves" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_work_gloves.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-chem_resistant_gloves" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_chem_gloves.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- 3. BODY: Lab Coat, Aprons, Reflective Vest (Overlaps glove cuffs) -->
|
||||
<g id="layer-gear-cotton_coat_chem" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_cotton_coat.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-chem_resistant_apron" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_chem_apron.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-fire_resistant_apron_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_fire_resistant_apron.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-fire_resistant_apron" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_fire_resistant_apron.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-reflective_vest_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_reflective_vest.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- 4. MASK: Dust Mask (Grinding Distractor) -->
|
||||
<g id="layer-gear-dust_mask_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_dust_mask.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- 5. EYES: Safety Goggles (Clear Grinding Protective Glasses) -->
|
||||
<g id="layer-gear-safety_goggles_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_safety_glasses.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-chem_splash_goggles" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_chem_splash_goggles.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- 6. HEARING: Acoustic Earmuffs (Grinding & Chemical) -->
|
||||
<g id="layer-gear-hearing_protection_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_earmuffs.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-hearing_protection_chem" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_earmuffs.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- 7. HEAD: Safety Helmet (Yellow Hard Hat) -->
|
||||
<g id="layer-gear-safety_helmet_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_safety_helmet.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-safety_helmet_chem" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_safety_helmet.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
<!-- 8. FACE: Face Shield (Grinding & Chemical Visor) -->
|
||||
<g id="layer-gear-face_shield_grind" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_face_shield.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
<g id="layer-gear-face_shield_chem" style="display: none;" filter="url(#gearShadow)">
|
||||
<image href="images/gear/gear_3d_face_shield.png" x="0" y="0" width="682" height="1024" preserveAspectRatio="xMidYMid meet" />
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
`;
|
||||
|
||||
this.updateLayers();
|
||||
}
|
||||
|
||||
updateLayers() {
|
||||
updateLayers(newlyEquippedId = null) {
|
||||
if (!this.container) return;
|
||||
|
||||
const imgEl = document.getElementById('avatar-step-img');
|
||||
if (imgEl) {
|
||||
const targetSrc = this.getStepImagePath();
|
||||
if (imgEl.getAttribute('src') !== targetSrc) {
|
||||
imgEl.src = targetSrc;
|
||||
const allGearIds = [
|
||||
'safety_helmet_chem', 'safety_helmet_grind',
|
||||
'chem_splash_goggles', 'safety_goggles_grind', 'face_shield_chem', 'face_shield_grind', 'tinted_sunglasses', 'dust_mask_grind',
|
||||
'hearing_protection_chem', 'hearing_protection_grind',
|
||||
'chem_resistant_gloves', 'standard_work_gloves', 'work_gloves_grind', 'leather_grinding_gloves', 'disposable_latex_gloves',
|
||||
'chem_resistant_apron', 'cotton_coat_chem', 'flame_resistant_jacket', 'reflective_vest_grind', 'fire_resistant_apron_grind', 'fire_resistant_apron',
|
||||
'chem_safety_shoes', 'steel_toe_boots_grind', 'running_sneakers'
|
||||
];
|
||||
|
||||
allGearIds.forEach(gearId => {
|
||||
const el = document.getElementById(`layer-gear-${gearId}`);
|
||||
if (el) {
|
||||
const isEquipped = this.equippedItems.has(gearId);
|
||||
el.style.display = isEquipped ? 'block' : 'none';
|
||||
if (isEquipped && gearId === newlyEquippedId) {
|
||||
el.classList.remove('gear-pop-glow');
|
||||
void el.offsetWidth;
|
||||
el.classList.add('gear-pop-glow');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user