Methods with Parameters & Constructor Chaining

Requirements

  1. Implement methods with parameters (e.g., collisionHandler(other, direction))
  2. Use super() to chain constructors for parent initialization

Evidence


Constructor Chaining with super()

Ghost.js Constructor

import Enemy from '@assets/js/GameEnginev1.1/essentials/Enemy.js';

class Ghost extends Enemy {
    constructor(data, gameEnv) {
        super(data, gameEnv);  // ← Constructor Chaining: initializes parent Enemy
        this.followSpeedFactor = data?.followSpeedFactor ?? 0.4;
        this.followStopDistance = data?.followStopDistance ?? 8;
        this._hasTriggeredDeath = false;
    }
}

How it meets the requirement:

  • super(data, gameEnv): Passes parameters to parent Enemy class constructor
  • Parent Initialization: Enemy class sets up collision detection, position, velocity
  • Child-Specific Properties: After super(), adds Ghost-specific properties

DeathBarrier.js Constructor

import Barrier from '@assets/js/GameEnginev1.1/essentials/Barrier.js';

class DeathBarrier extends Barrier {
    constructor(data, gameEnv) {
        super(data, gameEnv);  // ← Constructor Chaining: initializes parent Barrier
        this._hasTriggeredDeath = false;
        
        // Add static state management for grace period
        if (!DeathBarrier.levelStartTime) {
            DeathBarrier.levelStartTime = new Date();
        }
    }
}

How it meets the requirement:

  • super(data, gameEnv): Initializes parent Barrier’s collision and positioning
  • Parameters: data object contains configuration; gameEnv provides game context
  • Extended Initialization: Adds static properties for grace period tracking

SpriteSheetCoin.js Constructor

import Coin from '@assets/js/GameEnginev1.1/Coin.js';

class SpriteSheetCoin extends Coin {
    constructor(data = null, gameEnv = null) {
        super(data, gameEnv);  // ← Constructor Chaining: initializes parent Coin
        
        this.spriteImagePath = data?.spriteImagePath || null;
        this.spriteImage = null;
        this.isImageLoaded = false;
        this.spriteFrames = data?.spriteFrames || { rows: 1, columns: 1, frameIndex: 0 };
        this.fallbackToCircle = data?.fallbackToCircle !== false;
        
        if (this.spriteImagePath) {
            this.loadImage();
        }
    }
}

How it meets the requirement:

  • super(data, gameEnv): Initializes parent Coin class with position and collection logic
  • Default Parameters: Uses data = null, gameEnv = null for flexibility
  • Optional Chaining: Uses data?.spriteImagePath to safely access nested properties

Methods with Multiple Parameters

Ghost.js - followPlayer() Method

followPlayer(player) {  // ← Parameter: player object
    if (!player) return;

    const ghostCenter = this.getCenter();
    const playerCenter = typeof player.getCenter === 'function'
        ? player.getCenter()
        : { x: player.x || 0, y: player.y || 0 };

    const dx = playerCenter.x - ghostCenter.x;
    const dy = playerCenter.y - ghostCenter.y;
    const distance = Math.hypot(dx, dy);

    if (distance <= this.followStopDistance) {
        this.velocity.x = 0;
        this.velocity.y = 0;
        return;
    }

    const baseSpeed = player?.xVelocity || (this.gameEnv?.innerWidth || 800) / 2000;
    const speed = Math.max(0.3, baseSpeed * this.followSpeedFactor);
    const nx = dx / distance;
    const ny = dy / distance;

    this.position.x += nx * speed;
    this.position.y += ny * speed;

    if (Math.abs(dx) >= Math.abs(dy)) {
        this.direction = dx >= 0 ? 'right' : 'left';
    } else {
        this.direction = dy >= 0 ? 'down' : 'up';
    }
}

How it meets the requirement:

  • Parameter: Accepts player object and checks its type
  • Multiple Operations: Uses player data to calculate direction and distance
  • Return Value: Modifies ghost position based on player location

DeathBarrier.js - handleCollision() with Multiple Checks

update() {  // Overridden method with collision logic
    super.update();

    if (this._hasTriggeredDeath) return;  // ← Guard clause with state parameter

    const player = this.gameEnv?.gameObjects?.find(obj => obj.constructor?.name === 'Player');
    if (!player || !player.canvas || !this.canvas) return;  // ← Multiple parameter checks

    this.isCollision(player);  // ← Pass player to collision method

    if (this.collisionData?.hit) {
        // Check grace period from level start time
        const timeSinceLevelStart = new Date() - DeathBarrier.levelStartTime;
        if (timeSinceLevelStart < 500) {
            console.log('[MazeDebug] Grace period active:', timeSinceLevelStart, 'ms');
            return;  // Grace period still active
        }

        this._hasTriggeredDeath = true;
        player.isDead = true;

        console.log('[MazeDebug] DeathBarrier hit player:', this.canvas?.id || this.id || 'unknown');
        console.log('[MazeDebug] Player Position:', player.x, player.y);

        try {
            showDeathScreen(player, 'You got lost in the maze.');
        } catch (error) {
            console.error('DeathBarrier failed to show death screen:', error);
            this._hasTriggeredDeath = false;
            player.isDead = false;
        }
    }
}

How it meets the requirement:

  • Multiple Parameters: Checks player object’s multiple properties
  • Parameter Passing: Passes player to isCollision() for collision detection
  • Complex Logic: Uses parameters to determine game state and behavior

GameLevelOutside.js - applyPlayerSprite() with Parameters

const applyPlayerSprite = (player, skinKey) => {  // ← Two parameters: player object, skinKey string
    const spriteSrc = getPlayerSpriteSrc(skinKey);
    if (!player || !spriteSrc) return;
    if (player.spriteData?.src === spriteSrc) {
        setStoredPlayerSkinKey(skinKey);
        return;
    }

    const newSpriteSheet = new Image();
    newSpriteSheet.onload = () => {
        player.spriteSheet = newSpriteSheet;  // ← Modify player object based on parameters
        player.spriteReady = true;
        player.spriteData = { ...(player.spriteData || {}), src: spriteSrc };
        player.data = player.spriteData;
        player.frameIndex = 0;
        player.frameCounter = 0;
        player.resize();
        setStoredPlayerSkinKey(skinKey);
    };
    newSpriteSheet.onerror = (error) => {
        console.warn('Failed to load player spritesheet:', spriteSrc, error);
    };
    newSpriteSheet.src = spriteSrc;
};

How it meets the requirement:

  • Multiple Parameters: Takes player object and skinKey string
  • Parameter Usage: Uses both parameters to select and apply sprite
  • State Modification: Changes player properties based on parameters

Summary

Constructor Chaining - All classes use super() to initialize parents
Parameter Passing - Methods accept 1-2+ parameters for flexibility
Parameter Usage - Parameters modify object state and behavior
Type Safety - Parameter checks ensure valid data before use
Return Values - Methods return appropriate values or modify state