<commit_before>
import Entity, {EntityOptions, EntityLogicInput, EntityLogicOutput, EntityState} from '../Framework/Entity'
import KeyboardWatcher from '../Toolbox/KeyboardWatcher'

/** Options for creating a tank. */
export interface TankOptions extends EntityOptions {
  entityState: TankState
}

/**
 * It's a full blown tank!
 */
export default class Tank extends Entity {
  static type = 'Nanoshooter/Entities/Tank'

  /** Default tank art asset. */
  protected artPath: string = 'art/tanks/alpha/tank-alpha.obj'

  /** Array of all meshes related to this tank. */
  protected meshes: BABYLON.Mesh[]

  /** Parent tank body. Other tank components are children of this core unit. */
  protected chassis: BABYLON.Mesh

  /** Top-mounted gun on a swivel. */
  protected turret: BABYLON.Mesh

  /** Monitor keyboard activity for tank controls. */
  protected keyboardWatcher: KeyboardWatcher

  /** Whether or not this tank will respond to keyboard input and the like. */
  protected playerControlled = false

  /** Position the tank will start at. */
  protected startingPosition: BABYLON.Vector3

  /** Camera for this tank. */
  protected camera: BABYLON.TargetCamera

  /**
   * Construct a tank.
   */
  constructor(options: TankOptions) {
    super(options)
    const {entityState: tankState} = options

    // Starting position.
    if (tankState.position) {
      const p = tankState.position
      this.startingPosition = new BABYLON.Vector3(p[0], p[1], p[2])
    }

    // Determine player controlled state.
    this.playerControlled = !!tankState.playerControlled

    // If this tank is player controlled, establish a keyboard watcher.
    if (this.playerControlled) {
      this.keyboardWatcher = new KeyboardWatcher({
        keyNames: {
          'north': 'w',
          'east': 'd',
          'south': 's',
          'west': 'a',
        }
      })
    }

    // Load the tank obj from the art path specified in entity state, or use the default.
    this.loadTank(tankState.artPath || this.artPath)

      // When the tank is done loading.
      .then(() => {

        // Create the tank's camera – all tanks have a camera, it just might not be active.
        this.camera = new BABYLON.TargetCamera(`tank-camera-${this.id}`, BABYLON.Vector3.Zero(), this.stage.scene)
        this.camera.lockedTarget = this.chassis
        this.camera.position = this.chassis.position.add(new BABYLON.Vector3(0, 80, -40))

        // If the tank is player controlled.
        if (this.playerControlled) {

          // Create, position, and activate its camera.
          this.stage.scene.swithActiveCamera(this.camera)
        }
      })
  }

  /**
   * Load tank art (.obj file) into the scene.
   */
  loadTank(path: string): Promise<void> {
    return this.loader.loadObject({path}).then(loaded => {
      this.meshes = loaded.meshes

      // Find the right meshes.
      this.chassis = this.meshes.find(mesh => /Chassis/i.test(mesh.name))
      this.turret = this.meshes.find(mesh => /Turret/i.test(mesh.name))

      // Attach the turret to the chassis.
      this.turret.parent = this.chassis;

      // Assume the starting position.
      if (this.startingPosition)
        this.chassis.setAbsolutePosition(this.startingPosition.add(new BABYLON.Vector3(0, 10, 0)))

      // Apply physics.
      this.chassis.physicsImpostor = new BABYLON.PhysicsImpostor(
        this.chassis,
        BABYLON.PhysicsImpostor.BoxImpostor,
        {mass: 10, restitution: 0.1},
        this.stage.scene
      )
    })
  }

  /**
   * Run the tank's game logic.
   */
  logic(input: EntityLogicInput): EntityLogicOutput {

    // If meshes are loaded.
    if (this.meshes) {

      // Aim the tank's gun turret toward the user's cursor.
      if (this.stage.pick.hit) this.aimTurret(this.stage.pick.pickedPoint)

      // If the keyboard watcher is active.
      if (this.keyboardWatcher) {

        // Get the direction that the user wishes to move in.
        const desiredMovement = this.ascertainDesiredMovementVector()

        // Turn the tank chassis toward the desired movement vector.
        this.turnChassis(desiredMovement || this.lastDesiredMovementVector)

        // Gas the tank when movement keys are pressed.
        this.accelerateWhenMoving(desiredMovement)
      }
    }

    return
  }

  /** Remember our last movement direction so we face the same way when we stop. Intialized facing south. */
  protected lastDesiredMovementVector = new BABYLON.Vector3(0, 0, -1)

  /**
   * Get the direction in which the player wishes to move.
   */
  protected ascertainDesiredMovementVector(): (BABYLON.Vector3) {
    const vector = BABYLON.Vector3.Zero()
    const status = (label: string) => this.keyboardWatcher.status[label]

    if (!status('north') && !status('east') && !status('south') && !status('west'))
      return null

    if (status('north')) vector.z += 1
    if (status('east'))  vector.x += 1
    if (status('south')) vector.z -= 1
    if (status('west'))  vector.x -= 1

    this.lastDesiredMovementVector = vector
    return vector
  }

  protected speed = 5
  protected speedVector = new BABYLON.Vector3(this.speed, this.speed, this.speed)

  /**
   * Accelerate the tank when movement keys are pressed.
   */
  protected accelerateWhenMoving(desiredMovement: BABYLON.Vector3) {
    if (!desiredMovement) return
    this.chassis.physicsImpostor.setLinearVelocity(
      desiredMovement.multiply(this.speedVector)
    )
  }

  /**
   * Orient the chassis in a given direction.
   */
  protected turnChassis(direction: BABYLON.Vector3) {
    const point = this.chassis.position.add(direction)
    this.chassis.lookAt(point, 0, 0, 0)
  }
<commit_msg>Basic tank movement<commit_after>
import Entity, {EntityOptions, EntityLogicInput, EntityLogicOutput, EntityState} from '../Framework/Entity'
import KeyboardWatcher from '../Toolbox/KeyboardWatcher'

/** Options for creating a tank. */
export interface TankOptions extends EntityOptions {
  entityState: TankState
}

/**
 * It's a full blown tank!
 */
export default class Tank extends Entity {
  static type = 'Nanoshooter/Entities/Tank'

  public static get TURN_RIGHT(): number { return 1; }
  public static get TURN_LEFT(): number { return -1; }
  public static get MOVE_FORWARD(): number { return 1; }
  public static get MOVE_BACKWARD(): number { return -1; }

  /** Default tank art asset. */
  protected artPath: string = 'art/tanks/alpha/tank-alpha.obj'

  /** Array of all meshes related to this tank. */
  protected meshes: BABYLON.Mesh[]

  /** Parent tank body. Other tank components are children of this core unit. */
  protected chassis: BABYLON.Mesh

  /** Top-mounted gun on a swivel. */
  protected turret: BABYLON.Mesh

  /** Monitor keyboard activity for tank controls. */
  protected keyboardWatcher: KeyboardWatcher

  /** Whether or not this tank will respond to keyboard input and the like. */
  protected playerControlled = false

  /** Position the tank will start at. */
  protected startingPosition: BABYLON.Vector3

  /** Camera for this tank. */
  protected camera: BABYLON.TargetCamera

  /** Movement attributes */
  protected speedMax = 5
  protected speedTurn = 3
  protected chassisFacing = 0
  protected turnSpeed = Math.PI / 180

  /**
   * Construct a tank.
   */
  constructor(options: TankOptions) {
    super(options)
    const {entityState: tankState} = options

    // Starting position.
    if (tankState.position) {
      const p = tankState.position
      this.startingPosition = new BABYLON.Vector3(p[0], p[1], p[2])
    }

    // Determine player controlled state.
    this.playerControlled = !!tankState.playerControlled

    // If this tank is player controlled, establish a keyboard watcher.
    if (this.playerControlled) {
      this.keyboardWatcher = new KeyboardWatcher({
        keyNames: {
          'accel': 'w',
          'right': 'd',
          'brake': 's',
          'left': 'a',
        }
      })
    }

    // Load the tank obj from the art path specified in entity state, or use the default.
    this.loadTank(tankState.artPath || this.artPath)

      // When the tank is done loading.
      .then(() => {

        // Create the tank's camera – all tanks have a camera, it just might not be active.
        this.camera = new BABYLON.TargetCamera(`tank-camera-${this.id}`, BABYLON.Vector3.Zero(), this.stage.scene)
        this.camera.lockedTarget = this.chassis
        this.camera.position = this.chassis.position.add(new BABYLON.Vector3(0, 80, -40))

        // If the tank is player controlled.
        if (this.playerControlled) {

          // Create, position, and activate its camera.
          this.stage.scene.swithActiveCamera(this.camera)
        }
      })
  }

  /**
   * Load tank art (.obj file) into the scene.
   */
  loadTank(path: string): Promise<void> {
    return this.loader.loadObject({path}).then(loaded => {
      this.meshes = loaded.meshes

      // Find the right meshes.
      this.chassis = this.meshes.find(mesh => /Chassis/i.test(mesh.name))
      this.turret = this.meshes.find(mesh => /Turret/i.test(mesh.name))

      // Attach the turret to the chassis.
      this.turret.parent = this.chassis;

      // Assume the starting position.
      if (this.startingPosition)
        this.chassis.setAbsolutePosition(this.startingPosition.add(new BABYLON.Vector3(0, 10, 0)))

      // Apply physics.
      this.chassis.physicsImpostor = new BABYLON.PhysicsImpostor(
        this.chassis,
        BABYLON.PhysicsImpostor.BoxImpostor,
        {mass: 10, restitution: 0.1},
        this.stage.scene
      )
    })
  }

  /**
   * Run the tank's game logic.
   */
  logic(input: EntityLogicInput): EntityLogicOutput {

    // If meshes are loaded.
    if (this.meshes) {

      // Aim the tank's gun turret toward the user's cursor.
      if (this.stage.pick.hit) this.aimTurret(this.stage.pick.pickedPoint)

      // If the keyboard watcher is active.
      if (this.keyboardWatcher) {
        
        
        this.handleKeyboardInput()
        
      }
    }

    return
  }


  /**
   * Determine what direction the tank should move and turn based on keyboard input
   *  
   */
  protected handleKeyboardInput() {
    const status = (label: string) => this.keyboardWatcher.status[label]
        
    var moveDir = 0
    var turnDir = 0
        
    if (status('accel') && status('brake')) {
      moveDir = 0
    } else {
      if (status('accel')) {
        moveDir = Tank.MOVE_FORWARD
      } else if (status('brake')) {
        moveDir = Tank.MOVE_BACKWARD
      }
    }

    if (status('right') && status('left')) {
      turnDir = 0
    } else {
      if (status('right')) {
        turnDir = Tank.TURN_RIGHT
      } else if (status('left')) {
        turnDir = Tank.TURN_LEFT
      }
    }
    this.moveTank(moveDir, turnDir)
  }

  /**
   * Moves the tank
   *  -Currently applies basic movement (lookAt and setLinearVelocity) 
   *   -To be replaced with calls to physics methods (turn wheels)
   */
  protected moveTank(moveDir: number, turnDir: number) {
    var speed = 0
    
    if (turnDir == 0 && moveDir == 0) {
      return
    } else if (moveDir != 0) {
      if (turnDir == 0) { 
        /** Full power to both treads */

        //Basic movement
        speed = moveDir * this.speedMax
      } else {
        /** Full power to ONE tread, Slightly less to other */

        //Basic Movement
        speed = moveDir * this.speedMax
        this.chassisFacing += turnDir * moveDir * this.turnSpeed
      }
    } else {
      /** Full power to ONE tread, Minimum power to other */

      //Basic Movement
      speed = this.speedTurn
      this.chassisFacing += turnDir * this.turnSpeed
    }

    //Applies Basic Movement
    const directionVector = new BABYLON.Vector3(Math.sin(this.chassisFacing),0,Math.cos(this.chassisFacing))
    this.chassis.lookAt(this.chassis.position.add(directionVector),0,0,0)

    if (speed != 0) {
      const speedVector = new BABYLON.Vector3(speed,0,speed)
      this.chassis.physicsImpostor.setLinearVelocity(directionVector.multiply(speedVector))
    }
  }

{'fraction_non_alphanumeric': 0.11459799619110707, 'fraction_numerical': 0.007120973751759543, 'mean_word_length': 2.585040071237756, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3701757', 'n_tokens_mistral': 3894, 'n_tokens_neox': 3637, 'n_words': 1017}