JIT
Jak 3 but ai
Make a game of this: WASD - move in that direction with a small tilt, run at full speed with a large tilt. The more the stick is away from the center, the greater the speed, though there's only 2 different movement speeds available. Arrow keys - move the camera. Left rotates the camera clockwise (from north to east), right rotates the camera counterclockwise (from north to west). Up moves the camera closer in and angles it upwards slightly. Down moves the camera farther away and angles it downward slightly. The more the stick is moved away from the center, the faster the movement. The up and down orientations have a limit on how far they'll go. The configuration settings can change how this behaves. Spacebar - a basic jump. Somehow, there's got to be a way to cross that gap to get on that platform over there. There aren't any invisible lines you can just walk across. Spacebar then Spacebar while in mid-air near the top of a jump - double jump. Okay, so maybe there are invisible lines after all! How else can one jump in mid-air, jumping even higher? Or maybe land appears directly below for an instant too short to see but just enough to allow for a jump? Flash freeze doesn't show anything else. Hmm. Quite the puzzle we have! , - punch. Look out! That scorpion metal head is about to sting Jak! Punch that skull gem out of it! Be careful with this move though - never use it near the edge of a cliff, unless you feel like falling forever. Use it in open areas against a single enemy. It's also the stronger of the basic attacks, but only on certain things such as vehicles (yes, you can punch marauder vehicles to destroy them, and more quickly). Spacebar then , near the top of a jump - smashing dive. Glass barrier on the floor? Just break it! This can also be used to smash enemies and destroy vehicles, or take a leap of faith in the water below for a heroic 200-foot dive into the lake below! V - spin attack. I'm getting dizzy doing this! This is the best of the normal attacks to use as it defeats multiple things at once. For maximum effect, this works best in mid-air as it's easier to get the hits in without being hit. Spacebar, Spacebar, then V - a guided long jump. This is the long, guided jump, the second-longest jump there is. To maximize the jump distance, do a normal jump as usual then, upon falling for about 100 to 250 milliseconds, jump again with another Spacebar. Now, about 1/4 to 1/2 of the way down from the top of this jump, use the spin attack. The falling abruptly stops and another fall begins but you're still moving forward so gives that slight bit of extra distance. This, when each button is pressed at the top of the jump, allows for a slightly higher jump than a regular double jump. Q - duck. Whew! That robot hoverbot shot just passed right above me and I wasn't hit by it! That was close! Q then Spacebar while still ducking - high jump. How does one jump 15 feet high? This is useful for getting up those really high ledges. Under the right conditions, it's easier to use this than it is the launch jump with the jet board. Use this method whenever possible. Q then Spacebar while still ducking then V at the top of the jump - highest jump. It may not seem like it, but by using the spin attack at the top of the high jump, you actually gain a slight bit of extra height. There are times where this slight extra boost is all that's really needed. Q then , - uppercut. Aerial robot above? Just give it the ol' uppercut and put a good dent in it. Q while moving - roll. Rolling has almost no use by itself. So, then, why roll? I guess maybe to roll down that slope for some crazy speed where the camera can't keep up any more... or maybe not. Q while moving then Spacebar - rolling long jump. This is the longest of all the jumps. Start the roll about 2 Jak heights away from the edge of the gap you're trying to cross, and you'll make the big, long jump, starting a little before the edge of the gap is reached. When used in open areas, this is the fastest way to travel on foot, roughly 30% faster. Vehicles, including the jet board, are still faster (the jet board only slightly so, but more risky to use in towns). C - enter a vehicle or get on a leaper lizard. Don't want to be on foot or in water and would rather take a form of transportation of sorts? Go for it! It's tiring being on foot! 1 - transform. See the Dark Jak and Light Jak controls for more details. I - bring out and cycle through the red guns. Bang, bang! Red guns tend to have short range, but have a wide area of coverage to compensate for the short range. They are best used when those blasted metal heads are in front, in large numbers, and close. Pressing I once brings out the scatter gun. Pressing it again brings out the wave concussor. Pressing it yet again brings out the plasmite RPG. One more press and it's back to the scatter gun. K - bring out and cycle through the yellow guns. Zzzzeeewwwwww! Yellow guns tend have long range and also have greater ammo but go through ammo more quickly. Pressing I once brings out the blaster. Pressing it again brings out the beam reflexor. Pressing it a third time brings out the gyro burster. Another press and it's back to the blaster. J - bring out and cycle through the blue guns. Pa-pa-pa-pa-pa-pow! Blue guns tend have a rapid firing rate, eating up ammo very quickly by their nature making them great for taking out large quantities of metal heads. Pressing J once brings out the vulcan fury. Pressing it again brings out the arc welder. Pressing J yet again brings out the needle lazer. Press it once more to return to the vulcan fury. L - bring out and cycle through the dark guns. Kaboom! Dark guns, having a dark magenta color, have little in the way of ammo capacity (just 20 shots at the most), but they are immensely powerful to compensate, able to destroy most metal heads in a single blow with surrounding metal heads also going down for the count. Pressing L once brings out the peace maker. Pressing it again brings out the mass inverter. Pressing it yet again brings out the supernova. Press it again and it's back to the peace maker where it all began. Hold J, K, L, or I for 2 seconds - holsters the gun. At last the metal heads are gone and the mission is done, so I don't need the gun any more... or do I? There are some places that are forever in combat. W - fire a single shot from the current gun. If no gun is out, fires the most recently used gun. W held - charge or repeatedly fire the current gun, if applicable. Some guns require charging to use effectively, others can be shot repeatedly. Enter - bring up the wheel menu. Say, what sort of fancy customization options do I get? See section 3.6 for details on the options and section 4 for the secrts. Backspace - pause the game. Wai- (game paused)! Need to run to the bathroom or grab a quick drink... or need to bypass something especially difficult requiring fine timing and control? Pausing the game has plenty of uses, both directly and indirectly.

Okay, let's start building the foundation for this game. We'll begin with the basic scene setup, a player representation, WASD movement with two speeds (using Shift for the faster speed), a basic jump, and camera controls using OrbitControls as a placeholder.

This sets up:

  1. An HTML page (index.html) with an import map for Three.js and basic instructions.
  2. CSS (style.css) for basic page layout.
  3. A main script (main.js) initializing the Three.js scene, renderer, camera, lighting, and OrbitControls. It also creates instances of the InputHandler, PlayerController, and World.
  4. An InputHandler.js class to track keyboard inputs (WASD, Shift, Space).
  5. A PlayerController.js class representing the player with a capsule mesh. It handles movement based on camera direction, applies gravity, and performs a basic jump. It includes logic for two movement speeds (walk/run via Shift).
  6. A World.js class to create the ground plane and a simple test platform.

Next steps would involve implementing the custom camera controls (Arrow keys), more complex jump mechanics, collision detection with the platform, and the various attack/action moves.

Updated files
v1
All steps.

Okay, this is a significant undertaking! We'll structure the project with multiple modules to handle input, player logic, camera control, actions, guns, and the world environment.

This implementation will provide the structure and basic logic for most features, but many aspects (like detailed animations, precise collision, gun models/effects, vehicle physics, UI menus) will be simplified or represented by placeholders due to the complexity.

This comprehensive setup includes:

  1. main.js: Initializes everything, runs the main game loop, handles pausing.
  2. InputHandler.js: Manages all key presses, releases, and hold durations. Includes callbacks for specific key events.
  3. PlayerController.js: The core player logic with a state machine (PlayerState), movement physics (walk, run, roll, air control), gravity, basic collision detection (ground raycast, AABB for platforms), rotation, and placeholders for all actions (jump types, attacks). It delegates complex action logic to ActionManager and gun logic to GunManager.
  4. World.js: Creates the ground and several platforms (including a gap and a high ledge) and manages a list of collidable objects with pre-calculated bounding boxes.
  5. CameraManager.js: Replaces OrbitControls. Handles camera following, rotation (Arrow Left/Right), zoom/tilt (Arrow Up/Down) with limits and smoothing. Provides helper functions for player movement based on camera direction.
  6. ActionManager.js: Handles the logic for multi-key actions (double jump, high jump, rolling jump, smashing dive, uppercut, guided jump) using timers and state checks. Manages action durations.
  7. GunManager.js: Manages gun equipping, cycling (I, J, K, L), holstering (holding keys), firing (W), and displays a placeholder gun model. Includes basic bullet firing logic.
  8. UIManager.js: Updates the simple text display for the current gun and player state.
  9. index.html: Updated with more detailed controls info and divs for UI display.
  10. style.css: Basic styling.

This forms a much more complete, albeit still simplified, foundation for the game described. Many areas like collision response, animation integration, precise action timing/effects, and UI menus would need further refinement.

Updated files
v2
There is now a gray screen rather than anything.

Okay, a gray screen usually points to a JavaScript error preventing the scene from rendering or an issue with the camera setup. Let's add some checks and refine the code slightly.

One potential issue is in ActionManager.js where checkActionState uses a fixed 1/60 instead of the deltaTime. Let's fix that and add some basic error handling and logging to main.js.

Key changes:

  1. main.js:
    • Added console.log statements during initialization.
    • Wrapped renderer initialization and component initialization in try...catch blocks with error messages.
    • Wrapped the main animate loop content in a try...catch block to catch runtime errors and pause the game.
    • Added a check for excessively large deltaTime.
  2. ActionManager.js:
    • Passed deltaTime to checkActionState and used it for decrementing the timer.
    • Added more checks in handleJumpPress to ensure double jumps only happen from valid states.
  3. PlayerController.js:
    • Added checks for valid constructor arguments.
    • Wrapped player mesh creation in try...catch.
    • Initialized playerCollider Box3 immediately.
    • Improved setState logic with distinct "State Exit" and "State Enter" sections for clarity and flag management.
    • Refined handleInputAndStateTransitions to prioritize action/gun states and handle ground/air transitions more cleanly.
    • Improved applyMovement friction logic and added specific handling for aiming/firing/ducking states.
    • Refined updateRotation to handle aiming/firing lock correctly.
    • Improved checkCollisions:
      • Uses raycast origin slightly inside the capsule base.
      • Prioritizes raycast for ground detection.
      • Improved logic for landing on platforms vs. hitting walls.
      • Uses predicted Y position for ground check to prevent sinking.
      • Adjusted horizontal collision resolution logic (still basic, but slightly improved).
    • Ensured updatePosition applies velocity after collision resolution.
    • Added checks in action trigger methods (like jump) to prevent re-triggering while already in that state.
    • Adjusted spinAttack logic to capture stateTimer before the state change for guided/highest jump checks.

Now, run this version and check the browser's developer console (F12) carefully. Look for:

  • Any red error messages.
  • The console.log messages from main.js to see how far the initialization gets.
  • Any errors reported by the try...catch blocks.

This should help pinpoint where the process is failing.*

Updated files
v3
Remap Shooting gun to E.

Okay, let's remap the firing key from 'W' to 'E'.

Changes made:

  1. InputHandler.js:
    • Changed the fire getter to return this.isKeyPressed('e').
    • Removed the !this.isGunKeyHeldLong() check from the moveForward getter.
    • Removed 'w' from the general check in isGunKeyHeldLong.
  2. index.html:
    • Updated the control list text to show "E: Fire Gun".
  3. GunManager.js:
    • Minor adjustment in fireBullet to originate the bullet based on the player's forward direction and position rather than the placeholder gun mesh's transform, which gives more intuitive aiming.

Now, pressing 'E' should trigger the fire input, and 'W' will only be used for moving forward.*

Updated files
v4
Moving causes a error.

Okay, errors during movement often happen if calculations involving vectors (like direction or velocity) result in invalid numbers (NaN - Not a Number) or if an object reference is unexpectedly null or undefined.

Let's add some checks and logging, particularly around vector normalization and camera direction retrieval, as these are common culprits when movement input is involved.

Key Debugging Changes:

  1. main.js:
    • Clamped large deltaTime values to prevent physics explosions after pauses or slowdowns.
    • Added a check to skip frames if deltaTime is zero or negative.
  2. CameraManager.js:
    • Added checks in getForwardVector and getRightVector to prevent normalizing zero vectors (which results in NaN) and provide fallback directions.
  3. PlayerController.js:
    • applyMovement:
      • Added checks for NaN in the camera vectors retrieved from CameraManager.
      • Added a check (lengthSq() > 0.001) before normalizing moveDirection to prevent normalizing a zero vector.
      • Added a check before calculating targetRotationY using atan2.
      • Simplified friction application (removed the complex perpendicular velocity part for now, as it might be unstable).
    • updateRotation:
      • Added checks for NaN in targetAngle and the quaternions before calling slerp.
    • checkCollisions:
      • Refined ground check logic using predicted Y position.
      • Improved platform/wall collision resolution logic (still basic AABB push-out, but slightly refined).
      • Ensured ground snapping happens correctly.
    • updatePosition:
      • Added a check for NaN in the final velocityDt before adding it to the position. Reset velocity if NaN is found.
    • Action Methods (rollingLongJump, spinAttack): Ensured new THREE.Vector3 instances are created when getting world direction to avoid modifying shared vectors unintentionally.
  4. GunManager.js:
    • Refined logic for when the FIRING state can be entered and how it transitions back to AIMING or movement states.
    • Adjusted checkGunState to be less aggressive about enforcing AIMING state when the player is moving.
    • Fixed bullet animation loop to capture initial direction and added basic cleanup checks (though a proper manager is still recommended).

Run this version and check the console again when you try to move. The added checks and fallbacks should hopefully prevent NaN errors or log more specific information about where things are going wrong.

Updated files
v5
The game now crashes infinitely.

An infinite crash loop is serious and often points to an error that happens every frame, possibly related to physics instability, invalid calculations recurring, or incorrect state management.

Let's try the following:

  1. Add a safety break in the animation loop: Prevent the browser from completely freezing by stopping the loop after several consecutive errors.
  2. Simplify Collision Response: Temporarily revert the horizontal collision response in checkCollisions to a very basic "stop velocity" to see if the more complex push-out logic was causing instability.
  3. Add more detailed logging: Log values just before potentially problematic calculations (vector normalization, atan2, slerp).
  4. Ensure Camera lookAt is valid: Add a check to prevent lookAt if the camera position and target are too close.
  5. Refine Bullet Cleanup: Use setTimeout for the placeholder bullet removal for better reliability than the nested requestAnimationFrame.

Summary of Changes:

  1. main.js:
    • Added consecutiveErrors counter and MAX_CONSECUTIVE_ERRORS.
    • The animate loop now cancels requestAnimationFrame and sets isPaused = true if the error limit is reached, preventing a hard browser freeze.
    • Added logic to potentially restart the loop when unpausing if it was stopped due to errors.
  2. PlayerController.js:
    • checkCollisions: Replaced the horizontal push-out logic with a simpler version that just sets the corresponding velocity component (X or Z) to zero if moving towards an intersecting object horizontally. This is less physically accurate but potentially more stable for debugging.
    • Added more console.log statements (commented out by default) in applyMovement and updateRotation to trace values.
    • Added more isNaN checks for velocity components.
  3. CameraManager.js:
    • Added a check in update to prevent calling camera.lookAt if the camera position and the look-at target are extremely close, which can cause NaN issues in the camera's matrix.
    • Pre-allocated more vectors (_lookAtTarget, _desiredPosition, _desiredOffset) used within the update loop to potentially reduce garbage collection pressure slightly.
  4. GunManager.js:
    • Modified fireBullet to use setTimeout for scheduling the bullet's removal, which is generally more reliable for time-based events than nested requestAnimationFrame.
    • Added tracking of active bullet timeouts (this.activeBullets) and a basic cleanup method.
  5. World.js:
    • Added a check in addCollidable to warn if an invalid bounding box is created (e.g., for a mesh with no geometry or scale).

Now, when you run this:

  1. If the game crashes, it should pause itself after 10 consecutive errors, allowing you to inspect the console without the browser freezing completely.
  2. Check the console very carefully for the sequence of errors leading up to the pause. The added logging and checks might reveal NaN values or specific error messages related to collisions, rotations, or camera updates.
  3. The simplified collision response might prevent the crash if it was related to the push-out logic. If the game doesn't crash now (but horizontal collisions feel 'sticky'), it points towards the previous collision response being the culprit.*_
Updated files
v6
In the water, the controls change.

Okay, let's introduce a water mechanic. We'll define a water level, add a SWIMMING state, and modify controls and physics when the player enters the water.

We'll use a simple Y-level check for water detection and add a visual plane to represent the water surface.

Plan:

  1. World: Define waterLevel and add a water plane mesh.
  2. PlayerState: Add SWIMMING.
  3. InputHandler: Add getters for swimUp (Space) and swimDown (Q).
  4. PlayerController:
    • Add swimming physics properties (buoyancy, water drag, swim speeds).
    • Detect water entry/exit in update or checkCollisions and transition state.
    • Modify applyGravity for buoyancy when swimming.
    • Modify applyMovement to handle swimming controls (WASD horizontal, Space/Q vertical).
    • Disable ground-based actions (jump, punch, etc.) when swimming.

Key Changes:

  1. World.js:
    • Added waterLevel property.
    • Added createWaterSurface method to add a semi-transparent blue plane at the waterLevel.
  2. InputHandler.js:
    • Added swimUp (Space) and swimDown (Q) getters. These keys now serve dual purposes.
  3. PlayerController.js:
    • Added SWIMMING to PlayerState.
    • Added swimming physics properties (swimSpeedHorizontal, swimSpeedVertical, buoyancy, waterDrag).
    • Added isInWater flag.
    • Added checkWaterStatus method called in update to detect water entry/exit based on playerMesh.position.y and world.waterLevel, setting the SWIMMING state accordingly.
    • Renamed applyGravity to applyGravityAndBuoyancy. It now checks for the SWIMMING state and applies buoyancy force counteracting gravity, otherwise applies normal gravity.
    • Modified applyMovement:
      • Checks for SWIMMING state at the beginning.
      • If swimming, applies horizontal movement using swimSpeedHorizontal and water drag.
      • If swimming, checks input.swimUp and input.swimDown to modify velocity.y towards swimSpeedVertical.
      • Applies waterDrag to velocity.y as well when swimming.
    • Modified checkCollisions: It's now skipped if the player is swimming (to prevent unwanted ground snapping when underwater).
    • Modified updatePosition: Failsafe floor check is skipped if swimming.
    • Modified Action Triggers (jump, punch, spinAttack, etc.): Added checks at the beginning of most ground/air actions to return immediately if this.state === PlayerState.SWIMMING, effectively disabling them in water.
    • Modified jump: Added logic to allow a weaker jump out of the water if near the surface.
    • Stored player material in this.playerMaterial to change its color when entering/exiting water.
  4. ActionManager.js:
    • Modified handleJumpPress and handleDuckPress to simply return if swimming, as the vertical movement is handled directly in PlayerController.applyMovement.
  5. GunManager.js:
    • Added checks to prevent cycling guns (cycleGun) or firing (update) while player.state === PlayerState.SWIMMING.
    • Modified holsterGun to not force IDLE state if the player is swimming.
    • Modified checkGunState to return null if swimming.
  6. index.html: Updated control list for Space and Q.

Now, when the player's base goes below y = 0.5, they should enter the SWIMMING state (turning blue), WASD will control horizontal movement, Space will move them up, Q will move them down, and most other actions will be disabled. Gravity will be counteracted by buoyancy.*

Updated files
v7
The whole ground is now water. Half should be grass, and half should be Water. Also, the changed controls: , - dive. Get a big breath and explore the world below the waves. Surely there's some sunken orbs down there! Hold to sink down deeper or swim at the bottom. Jak will only go so far under then level off, something like 8 meters. Press repeatedly to control depth. V - dive. Okay, 2 buttons do the same thing. Now, pressing both buttons at once.... E - does nothing. Sorry, when in deep water, guns cannot be used. Consider jumping at the surface of the water and using the spin attack for the time being. 1 - does nothing. Sorry, when in deep water, Jak cannot transform. Find somewhere solid to transform. Q - does nothing. Need to duck? Just dive instead! You've got all that water below you to get out of the way - use it to your advantage! You don't get 3 axes of movement for nothing ya know!

Okay, let's adjust the world layout and refine the swimming controls according to your new specifications.

Plan:

  1. World: Split the ground into a grass half and a lower "seabed" half. Place the water surface only over the seabed half.
  2. InputHandler: Change swimDown (Q) to do nothing in water. Add diveSink mapped to , and V.
  3. PlayerController:
    • Modify swimming movement in applyMovement to use diveSink for downward movement, respecting the depth limit. Remove swimDown (Q) logic.
    • Ensure Transform (1) is disabled when swimming.
    • Ensure Q has no effect when swimming.

Key Changes Summary:

  1. World.js:
    • createSplitGround replaces createGround, making a grass plane on +X and a lower seabed plane on -X.
    • createWaterSurface now creates a plane only covering the -X side at waterLevel.
    • Platforms repositioned for the new layout.
  2. InputHandler.js:
    • Removed swimDown getter.
    • Added diveSink getter mapped to , and v.
  3. PlayerController.js:
    • Added diveSpeed property.
    • Added MAX_SWIM_DEPTH constant.
    • checkWaterStatus now also checks if the player is in the water area (position.x < 0).
    • applyMovement (Swimming): Uses input.diveSink to apply downward velocity (diveSpeed), respecting MAX_SWIM_DEPTH. Removed swimDown logic.
    • applyGravityAndBuoyancy (Swimming): Added check to prevent applying buoyancy if actively diving at max depth.
    • checkCollisions is skipped entirely when swimming unless near the seabed.
    • Added checkSeabedCollision for simple collision detection with the seabed when swimming deep.
    • handleTransform now explicitly checks for SWIMMING state and returns.
  4. ActionManager.js:
    • handlePunchPress and handleSpinPress now just return if swimming (the dive input is read directly in PlayerController.applyMovement).
    • handleDuckPress and handleDuckRelease now just return if swimming (Q does nothing in water).
  5. index.html: Updated controls list to reflect changes for Q, ',', V, 1, E, and Guns in water.

Now, the world should have a distinct land and water area. When in the water area (x < 0) and below the water surface (y < 0.5), swimming controls activate: Space swims up, , or V dives down (up to 8m below the surface), Q does nothing, Transform does nothing, Guns cannot be cycled or fired.

Updated files
v8
You can transform. 1 - transform into Dark Jak and return to normal. Jak turns into quite the monster, but only if he has some dark eco. What hideous acts do you have against those pesky metal heads? Spacebar then , while in mid-air - Dark Bomb. Unleash a massive shock wave that deals big damage to all around him in a modest radius of about 50 meters. , then Spacebar while on the ground - Dark Blast. Unleash an energy wave that deals big damage to all around him in a modest radius of about 50 meters. Q held then , - Dark Blast. Another button combination that does the same thing? This is different! C - Dark invisibility. When next to certain idols, for which is only needed in a certain area of the story for one mission, Jak turns invisible. No metal head, security device, etc. will ever know he was there. Jak turns into a transparent outline so you can at least see where you're going. It is possible to unlock invisibility in any location though, even in the middle of the desert with marauders bearing down at full speed. E - Dark Charge. Send a big blast of dark eco at something. It's really only needed to advance the story, hardly of use elsewhere. See that broken glass? Shatter it with Dark Charge and now you have easy passage into the next room.