Skip to content

Add null check for active cell in AIPilot.UpdateCellContent()#130

Open
Shombith03 wants to merge 43 commits into
claude/revamp-freestyle-mode-h1Il9from
claude/shape-signs-face-player-INiJp
Open

Add null check for active cell in AIPilot.UpdateCellContent()#130
Shombith03 wants to merge 43 commits into
claude/revamp-freestyle-mode-h1Il9from
claude/shape-signs-face-player-INiJp

Conversation

@Shombith03

Copy link
Copy Markdown
Contributor

Summary

Added a defensive null check for the active cell before attempting to access cell items in the UpdateCellContent() method of the AIPilot class.

Key Changes

  • Added null check for activeCell variable after retrieval from cellData.Cell
  • Updated the guard clause comment to reflect that it now also protects against unready cells
  • Prevents potential null reference exceptions when the cell is not yet initialized

Implementation Details

The new check if (activeCell == null) return; is placed immediately after retrieving the active cell and before accessing cellData.CellItems, ensuring the method exits gracefully if the cell data is not ready rather than throwing an exception downstream.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq

claude and others added 30 commits February 20, 2026 03:39
- Add GameplaySFXCategory enum with 11 categories (BlockDestroy, ShieldActivate,
  ShieldDeactivate, MineExplode, ProjectileLaunch, CrystalCollect, VesselImpact,
  GameEnd, ScoreReveal, PauseOpen, PauseClose) and PlayGameplaySFX method to
  AudioSystem with serialized AudioClip fields for each

UI sounds:
- ModalWindowManager: OpenView on ModalWindowIn, CloseView on ModalWindowOut
  (covers all 8+ modals: Profile, Settings, ArcadeConfig, HangarTraining, etc.)
- NavLink.OnClick: SwitchView sound on tab/view switching
- ArcadeScreen.ToggleView: SwitchView sound on Loadout/Explore toggle
- PauseMenu: PauseOpen/PauseClose gameplay SFX on show/hide
- PurchaseConfirmationModal.Confirm: Confirmed sound
- ArcadeGameConfigureModal: Confirmed on config confirm, LetsGo on start game
- All game launch buttons: LetsGo sound (ArcadeExploreView, ArcadeLoadoutView,
  DailyChallengeModal, FactionMissionModal, HangarTrainingModal)

Gameplay sounds:
- Prism.Explode/Implode: BlockDestroy SFX on block destruction
- PrismStateManager: ShieldActivate/ShieldDeactivate SFX on shield state changes
- Mine.Explode: MineExplode SFX
- Projectile.LaunchProjectile: ProjectileLaunch SFX
- VesselImpactor: CrystalCollect for crystal pickups, VesselImpact for prism/skimmer hits
- EndGameCinematicController: GameEnd on winner calculated, ScoreReveal on score display

https://claude.ai/code/session_01LT3ivtpFxXy6tx9SrQvP6o
Extends the GameplaySFXCategory with 4 new categories:
- GunFire, BoostActivate, Explosion, CreatureDeath

Gameplay sounds added:
- FireGunActionExecutor.Fire: GunFire SFX on weapon discharge
- ConsumeBoostActionExecutor.Consume: BoostActivate SFX on boost use
- ChargeBoostActionExecutor.BeginDischarge: BoostActivate SFX on charged boost release
- AOEExplosion.Detonate: Explosion SFX on area-of-effect detonation
- LifeForm.Die: CreatureDeath SFX when flora/fauna entities are killed

https://claude.ai/code/session_01LT3ivtpFxXy6tx9SrQvP6o
- GameCard: OptionClick on card click and favorite toggle
- SwitchToggle: OptionClick on toggle state change
- ProfileIconSelectButton: OptionClick on icon selection
- GameplayRewardButton: Confirmed on reward claim
- DailyRewardCard: Confirmed on free claim and ad watch reward

https://claude.ai/code/session_01LT3ivtpFxXy6tx9SrQvP6o
Wire new SFX categories (DriftStart, DriftEnd, EnergyGain, SpeedBurst)
into the gameplay systems that define the squirrel feel:
- Drift engage/disengage sounds in DriftActionSO and legacy DriftAction
- Energy gain chime when squirrel collects from NudgeShards
- Speed burst whoosh on ModifyVelocityActionSO activation
- Boost activate sound on simple BoostActionSO (was missing)

https://claude.ai/code/session_01LT3ivtpFxXy6tx9SrQvP6o
…efab

Generate WAV audio files for DriftStart, DriftEnd, EnergyGain, and SpeedBurst
sound effects using synthesized tones. Wire them into the AudioSystem prefab
so the serialized fields reference the new clips. Also explicitly declare the
other gameplay SFX slots in the prefab (currently unassigned).

https://claude.ai/code/session_01LT3ivtpFxXy6tx9SrQvP6o
All GameplaySFXCategory slots were set to {fileID: 0} (null), causing
PlayGameplaySFX to silently skip every sound. Map existing audio assets
to each category using best-fit matches from the Sounds library:

- BlockDestroy/VesselImpact → Crystal_Collision_Amplified
- CrystalCollect → Crystal_Glass
- MineExplode/Explosion → END GAME crash
- GameEnd → END GAME after crash
- ProjectileLaunch → Fly By 06_1
- GunFire → Click 12
- ScoreReveal → Data 24
- CreatureDeath → crys_enemlayer_var3_MN1-001
- ShieldActivate → Confirmed (menu)
- ShieldDeactivate/PauseClose → CloseView (menu)
- PauseOpen → OpenView (menu)
- BoostActivate → Upgrade (menu)

Also add debug warning log when a clip is still missing so it's no
longer silently swallowed.

https://claude.ai/code/session_01LT3ivtpFxXy6tx9SrQvP6o
…not yet assigned

cellData.Cell can be null when UpdateCellContent is called from Initialize()
before the game cell has been set up. Added null guard for activeCell.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
…scoring log

ShapeSignSpawner: Replaced dynamic Instantiate/Destroy with simple
enable/disable of pre-placed scene GameObjects. Signs are now positioned
and oriented by the designer in the editor.

ShapeSign: Added OnEnable reset — clears _selected state, re-enables
button interactability, snaps position back to base (undoing bob offset).

SinglePlayerFreestyleController: ClearPlayerTrails() called immediately
after SetPlayersActive() in OnCountdownTimerEnded so trail spawners
never run in the lobby. ShowSigns() call takes no arguments now.

ShapeDrawingManager: Enhanced FinishShape() log with par time, sample
count, waypoint count. Added TakeDebugScreenshot() (F12 key or wire to
UI button) — saves PNG to Application.persistentDataPath/Screenshots/.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
…reestyle teardown

Three critical performance fixes for explosion scaling:

1. PrismAOERegistry: Cap new prism hits to 48/frame per explosion.
   Previously 2000+ prisms destroyed in one frame (426ms spike).
   Now spreads across ~47 frames — unprocessed hits are re-found by
   the Burst spatial query on subsequent frames automatically.

2. PrismEffectsManager: Replace List with HashSet for active VFX tracking.
   Registration/unregistration was O(n) Contains + O(n) Remove per item.
   With 2000+ active VFX completing, the completion loop was O(n²).
   Now O(1) for all operations.

3. ExplosionImpactor: Cache and reuse _batchHitTracker HashSet instead
   of allocating new HashSet<int>(256) per explosion start.

Also merges the freestyle connecting screen fix (scene teardown safety):
- PrismEffectsManager._instanceDestroyed prevents auto-creating during teardown
- Null-propagation on EnsureInstance calls in PrismExplosion/PrismImplosion
- MinigameHUDView null check on playerScoreContainer

https://claude.ai/code/session_01FnQQDjDMDPfpEPvrvXNtJN
…racy + ghost line

Shape Orientation:
- Added shapeOrientationEuler field (default -90,0,0) to ShapeDrawingManager
  that rotates XY-plane shape waypoints to horizontal XZ plane so they face
  the top-down reveal camera correctly.
- All waypoint/position calculations now go through rotation-aware helpers
  (GetWorldWaypoint, GetAllWorldWaypoints, GetWorldPlayerStart).

Preview Cinematic Flow:
- StartShapeSequence now runs a two-phase flow:
  Phase 1 (PreviewSequence): place player at start → lock input (IsStationary)
    → draw ghost shape → camera flies to top-down overview → holds → transitions
    back to behind player → fires OnPreviewComplete event.
  Phase 2 (BeginDrawing): controller shows Ready button on OnPreviewComplete,
    player clicks Ready → controller calls BeginDrawing() → releases input,
    starts timer, spawns first crystal.
- SinglePlayerFreestyleController overrides OnReadyClicked_ to detect shape-
  prep state and call BeginDrawing() instead of normal countdown.

Input System Fix:
- Replaced UnityEngine.Input.GetKeyDown (legacy) with
  Keyboard.current[Key].wasPressedThisFrame (new Input System package).
- Screenshot key field changed from KeyCode to Key enum.

Accuracy Fix:
- Root cause: the legacy Input.GetKeyDown threw InvalidOperationException
  every frame in Update(), preventing SamplePlayerPosition() from ever running
  → 0 path samples → 0% accuracy.
- Also: accuracy calculation now uses rotation-aware waypoints matching actual
  crystal positions.

Ghost Line Fix:
- Ghost line now uses rotation-aware waypoints (was rendering in XY plane,
  invisible from most camera angles). Shader fallback chain added:
  Sprites/Default → Universal Render Pipeline/Unlit.

Other:
- Default shapeScale increased from 1 to 3 for better visibility.
- Added _drawingStarted flag to prevent Update from running guide line /
  sampling before BeginDrawing is called.
- Camera acquire/release extracted to helper methods (AcquireCamera,
  ReleaseCamera) reused by both preview and reveal cinematics.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
…hanger

- Use IVesselStatus via IVessel.VesselStatus instead of GetComponent
  (root cause of 0% accuracy / 0 path samples — component on different GO)
- Wire countdown timer into shape flow: Ready → countdown → BeginDrawing
  (vessel stays locked until countdown finishes)
- Compute player spawn from first waypoint (30u behind, facing crystal)
- Add SnowChanger prefab integration for directional shards
- Fix all null checks from Unity truthy to != null for interface type

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
ProfileModal was routing through the disabled PlayFab PlayerDataController
instead of the active UGS PlayerDataService, so name changes were silently
dropped. ArcadeProfileWidget and ProfileIconSelectView lacked a singleton
fallback for PlayerDataService (whose Awake detaches from hierarchy via
DontDestroyOnLoad, breaking serialized Inspector refs) and gated saves on
IsInitialized when the service is usable as soon as CurrentProfile exists.

Default display name changed from static "Pilot" to "Pilot#XXXX" with a
random 4-digit suffix so new players get unique names out of the box.

https://claude.ai/code/session_018uU3m2F5XVUMoynp8jxdM4
…-1ZgoJ

Claude/add missing sounds 1 zgo j
…teardown fix

The HashSet conversion broke explosion VFX. Reverted to original List-based
tracking with identical processing flow. The only addition is the
_instanceDestroyed pattern from the freestyle fix branch to prevent
NullReferenceExceptions during scene teardown.

The per-frame damage cap in PrismAOERegistry (48 hits/frame) and the
cached HashSet in ExplosionImpactor remain — those are the primary
performance wins.

https://claude.ai/code/session_01FnQQDjDMDPfpEPvrvXNtJN
The _instanceDestroyed flag + ?. null-propagation operators were
causing PrismExplosion/PrismImplosion VFX to silently skip registration
with PrismEffectsManager. VFX objects spawned from the pool with team
colors applied but never received frame updates, appearing as static
colored blocks replacing destroyed prisms.

- Remove _instanceDestroyed static flag and ResetStatics from PrismEffectsManager
- Revert EnsureInstance() to always auto-create if no instance exists
- Revert ?. to . on EnsureInstance().Register calls in PrismExplosion/PrismImplosion

https://claude.ai/code/session_01FnQQDjDMDPfpEPvrvXNtJN
…ale to 7

- Add ScriptableEventNoParam field (onReturnShapePrismsEvent) raised in
  ExitShapeMode before ClearTrails — follows SOLID by decoupling pool
  return from ShapeDrawingManager via SO event channel
- User wires EventListenerNoParam on shape prisms → Prism.ReturnToPool()
- Increase default shapeScale from 3 to 7 (lightning blocks too close)
- Move ClearTrails from ContinueFromReveal into ExitShapeMode (single
  exit path: raise event → return to pool → clear trail references)

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
ShapeSign: Remove bob/spin animation — signs stay exactly where placed
in the scene editor. No script modifies position at any point.

ShapeDrawingManager:
- shapeScale default → 10 (lightning crystals too close at smaller scale)
- Defensive StopSpawn() in BeginDrawing before releasing vessel
- Stop player (IsStationary = true) when shape completes
- Show EndShapeDetailHUD after reveal camera pan
- On exit: respawn player at shape start, freeze, snap camera, then
  fire OnFreestyleResumed so controller shows connecting panel
- Null _activeShape after using it for respawn position

EndShapeDetailHUD (new):
- Displays shape name, elapsed/par time, accuracy %, star rating
- Screenshot button → ShapeDrawingManager.TakeDebugScreenshot
- Exit button → hides HUD, exits shape mode
- User creates UI layout in GameCanvas, wires TMP_Text + Button refs

SinglePlayerFreestyleController:
- Add MiniGameHUD reference for connecting panel flow
- _returningFromShape flag: after shape exit, Ready enters lobby
  instead of calling SetPlayersActive again
- OnShapeDrawingFinished: stop vessel, snap camera, show connecting
  panel via miniGameHUD.ShowConnectingFlow()

MiniGameHUD:
- Add public ShowConnectingFlow() wrapper for ResetForReplay()

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
…t event

ShapeSign:
- Store position/rotation in Awake, enforce via LateUpdate every frame
- Nothing (physics, parent changes, other scripts) can move signs from
  their scene-placed positions
- OnSignButtonPressed uses _lockedPosition instead of transform.position

ShapeDrawingManager — removed ALL VesselPrismController manipulation:
- StartShapeSequence: fires onShapeGameModeStarted event instead of
  StopSpawn/ClearTrails. Just sets IsStationary = true.
- BeginDrawing: just sets IsStationary = false. Vessel's natural prism
  spawning takes over.
- HandleCrystalHit: removed StartSpawn/StopSpawn per-segment toggle.
  Keeps tracking/scoring logic only.
- FinishShape: removed StopSpawn. Just sets IsStationary = true.
- ExitShapeMode: removed ClearTrails. Uses onReturnShapePrismsEvent
  only (SO event for prism pool return).

New ScriptableEventNoParam field: onShapeGameModeStarted
- Fired when entering shape mode for a clean slate
- User wires EventListenerNoParam on prisms → ReturnToPool

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
- RevealSequence: hide vesselHUD when showing EndShapeDetailHUD
- ExitShapeMode: restore vesselHUD after cleanup
- Controller returning-from-shape: explicitly ShowHUD when re-entering
  lobby (ResetForReplay hides it, SetPlayersActive isn't called again)

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
LateUpdate now faces the sign toward Camera.main while keeping
position locked and the sign upright (zeroed Y look direction).
Removed the locked rotation that prevented any facing behavior.

Also: ShapeSignSpawner should be enabled in scene, its Signs list
populated with the sign GameObjects, and the "(Removed)" missing
script reference deleted from each sign.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
…d FreestyleSign

- Remove LateUpdate billboard rotation from ShapeSign — signs stay at their placed orientation
- Remove IsStationary=true from FinishShape and ExitShapeMode so player keeps flying after shape completion
- OnShapeDrawingFinished now teleports directly to lobby instead of showing connecting flow
- Remove dead _returningFromShape field and its OnCountdownTimerEnded branch
- Add FreestyleSign + FreestyleSignEvents for starting normal segment-spawner gameplay via a clickable sign
- Wire FreestyleSignEvents into SinglePlayerFreestyleController

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
…attern

Vessel/projectile collision activates the sign instead of a UI button.
Includes ResetTrigger() for lobby re-entry.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
ShapeSign & FreestyleSign:
- Switch from UI Button to OnTriggerEnter (same as ModeSelectTrigger)
- Lock position, billboard toward player camera in LateUpdate
- Add ResetTrigger() for lobby re-entry

SinglePlayerFreestyleController:
- Add _isFreestylePrep state so freestyle follows the same flow as
  shape mode: freeze player → teleport to spawn → connecting panel →
  ready button → countdown → start gameplay
- Route all null-shapeDef selections through StartFreestylePrep

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
ShapeSign & FreestyleSign:
- Remove all LateUpdate, _lockedPosition, billboard, and transform
  manipulation. Signs are purely editor-positioned, just enable/disable.

SinglePlayerFreestyleController:
- Move gameData.StartTurn() out of ExitLobby() into BeginFreestyle()
  and StartShapeMode() so systems are enabled BEFORE the turn event
  fires. LocalCrystalManager.OnEnable subscribes to OnMiniGameTurnStarted,
  so it must be enabled before StartTurn() for crystals (and downstream
  flora/fauna) to spawn.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
Root cause: modeTriggers in the scene ARE the HeartShapeSign prefab
instances (ModeSelectTrigger components added to them). EnterLobby()
was recalculating position/rotation/scale every time, overriding the
editor-placed transforms.

Fix:
- Remove all transform manipulation from EnterLobby() — just
  enable/disable and reset triggers
- Remove triggerRingRadius, triggerScale, triggerForwardOffset fields
- Restore ShapeSignSpawner field names to match scene serialized data
  (signEntries, ringRadius, height, faceCenter, signScale, origin)
- Signs spawn once on first ShowSigns(), then just toggle visibility

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
The ModeSelectTrigger components live directly on the sign prefab
instances in the scene. The controller just enables/disables them.
ShapeSignSpawner was a dead middleman with no effect (its GO was
disabled in the scene anyway).

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
Removed SpawnSigns() and all instantiation. Kept serialized field
names (signEntries, ringRadius, etc.) so scene data isn't orphaned.
ShowSigns/HideSigns now just toggle the prefab references directly.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
…s-vFvfH

Optimize explosion processing and reduce GC allocations
claude and others added 13 commits February 26, 2026 22:05
ShapeSignSpawner was unnecessary indirection. Signs are editor-placed
children of a parent GameObject. Controller now holds a signsParent
reference and toggles SetActive in EnterLobby/ExitLobby, same pattern
as modeTriggers. This fixes the two new signs not getting disabled.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
Resolved trivial conflict in MinigameHUDView.ClearPlayerList —
kept development's explicit null check style.

https://claude.ai/code/session_01Y1d7YaQBxppMr54ZGShjVq
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants