Skip to content

Commit 4521a88

Browse files
rjcorwinclaude
andcommitted
Implement Milestone 5 Phase 5c and Milestone 6: Platform Coordinate System
This commit implements the remaining ship navigation features and the critical platform coordinate system that allows players to ride along on moving ships. Phase 5c: Ship Movement & Navigation - Added 'navigable' tile property to all map tiles (water=true, land=false) - Ships can now distinguish navigable vs non-navigable terrain - End-to-end ship controls and movement verified Milestone 6: Platform Coordinate System - Phase 6a: Ship Boundary Detection * Added onShip tracking to Player type * Implemented checkShipBoundary() to detect when players enter/exit ship deck * Logs boarding/disembarking events * Stores ship-relative position when player is on ship - Phase 6b: Coordinate Transformation * Added shipRelativePosition field to track position relative to ship center * Calculates and maintains relative offset when player boards ship * Updates relative position as player moves around on deck - Phase 6c: Platform Movement (players ride along!) * Ship movement now propagates to all players on the ship * Local player moves with ship when onShip is set * Remote players move with ship based on their onShip state * Players maintain relative position on deck as ship moves Technical Details: - Ship boundary checking uses rectangular deck boundary (width/height from shipData) - Players automatically board when within deck bounds, leave when outside - Ship movement delta is applied to all passengers each frame - This works for any number of ships and players simultaneously Files Modified: - clients/mew-world/assets/maps/map1.tmj: Added navigable properties - clients/mew-world/src/types.ts: Added onShip field to Player - clients/mew-world/src/game/GameScene.ts: Added boundary detection and platform movement Players can now: 1. Walk onto a ship's deck and automatically board 2. Ride along as the ship moves through the world 3. Walk around on the ship's deck while it's moving 4. Walk off the ship to disembark 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 7f66ee8 commit 4521a88

File tree

3 files changed

+104
-8
lines changed

3 files changed

+104
-8
lines changed

clients/mew-world/assets/maps/map1.tmj

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,21 @@
4949
"name":"speedModifier",
5050
"type":"float",
5151
"value":0.8
52-
},
52+
},
5353
{
5454
"name":"terrain",
5555
"type":"string",
5656
"value":"sand"
57-
},
57+
},
5858
{
5959
"name":"walkable",
6060
"type":"bool",
6161
"value":true
62+
},
63+
{
64+
"name":"navigable",
65+
"type":"bool",
66+
"value":false
6267
}]
6368
},
6469
{
@@ -68,16 +73,21 @@
6873
"name":"speedModifier",
6974
"type":"float",
7075
"value":1
71-
},
76+
},
7277
{
7378
"name":"terrain",
7479
"type":"string",
7580
"value":"grass"
76-
},
81+
},
7782
{
7883
"name":"walkable",
7984
"type":"bool",
8085
"value":true
86+
},
87+
{
88+
"name":"navigable",
89+
"type":"bool",
90+
"value":false
8191
}]
8292
},
8393
{
@@ -87,16 +97,21 @@
8797
"name":"speedModifier",
8898
"type":"float",
8999
"value":0.5
90-
},
100+
},
91101
{
92102
"name":"terrain",
93103
"type":"string",
94104
"value":"water"
95-
},
105+
},
96106
{
97107
"name":"walkable",
98108
"type":"bool",
99109
"value":true
110+
},
111+
{
112+
"name":"navigable",
113+
"type":"bool",
114+
"value":true
100115
}]
101116
},
102117
{
@@ -106,16 +121,21 @@
106121
"name":"speedModifier",
107122
"type":"float",
108123
"value":0
109-
},
124+
},
110125
{
111126
"name":"terrain",
112127
"type":"string",
113128
"value":"concrete"
114-
},
129+
},
115130
{
116131
"name":"walkable",
117132
"type":"bool",
118133
"value":false
134+
},
135+
{
136+
"name":"navigable",
137+
"type":"bool",
138+
"value":false
119139
}]
120140
}],
121141
"tilewidth":32

clients/mew-world/src/game/GameScene.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export class GameScene extends Phaser.Scene {
2727
private interactionPrompt!: Phaser.GameObjects.Text;
2828
private controllingShip: string | null = null;
2929
private controllingPoint: 'wheel' | 'sails' | null = null;
30+
private onShip: string | null = null; // Track if local player is on a ship
31+
private shipRelativePosition: { x: number; y: number } | null = null; // Position relative to ship center
3032

3133
constructor() {
3234
super({ key: 'GameScene' });
@@ -300,6 +302,7 @@ export class GameScene extends Phaser.Scene {
300302
lastUpdate: update.timestamp,
301303
velocity: update.velocity,
302304
platformRef: update.platformRef,
305+
onShip: null,
303306
};
304307

305308
this.remotePlayers.set(update.participantId, player);
@@ -448,9 +451,44 @@ export class GameScene extends Phaser.Scene {
448451
this.lastPositionUpdate = time;
449452
}
450453

454+
// Check for ship boundary detection (Phase 6a)
455+
this.checkShipBoundary();
456+
451457
// Check for ship control point interactions
452458
this.checkShipInteractions();
453459

460+
// Phase 6c: Update ships and move players on ships
461+
this.ships.forEach((ship) => {
462+
const dx = ship.targetPosition.x - ship.sprite.x;
463+
const dy = ship.targetPosition.y - ship.sprite.y;
464+
const distance = Math.sqrt(dx * dx + dy * dy);
465+
466+
if (distance > 1) {
467+
// Calculate ship movement delta
468+
const factor = Math.min(1, (delta / 1000) * 5);
469+
const shipDx = dx * factor;
470+
const shipDy = dy * factor;
471+
472+
// Move ship
473+
ship.sprite.x += shipDx;
474+
ship.sprite.y += shipDy;
475+
476+
// Move local player if on this ship
477+
if (this.onShip === ship.id && this.shipRelativePosition) {
478+
this.localPlayer.x += shipDx;
479+
this.localPlayer.y += shipDy;
480+
}
481+
482+
// Move remote players if on this ship
483+
this.remotePlayers.forEach((player) => {
484+
if (player.onShip === ship.id) {
485+
player.sprite.x += shipDx;
486+
player.sprite.y += shipDy;
487+
}
488+
});
489+
}
490+
});
491+
454492
// Interpolate remote players toward their target positions
455493
this.remotePlayers.forEach((player) => {
456494
const dx = player.targetPosition.x - player.sprite.x;
@@ -466,6 +504,43 @@ export class GameScene extends Phaser.Scene {
466504
});
467505
}
468506

507+
private checkShipBoundary() {
508+
// Check if player is within any ship's deck boundary
509+
let foundShip: string | null = null;
510+
let shipRelativePos: { x: number; y: number } | null = null;
511+
512+
this.ships.forEach((ship) => {
513+
// Calculate distance from ship center
514+
const dx = this.localPlayer.x - ship.sprite.x;
515+
const dy = this.localPlayer.y - ship.sprite.y;
516+
517+
// Check if within deck boundary (rectangular area)
518+
const halfWidth = ship.deckBoundary.width / 2;
519+
const halfHeight = ship.deckBoundary.height / 2;
520+
521+
if (Math.abs(dx) <= halfWidth && Math.abs(dy) <= halfHeight) {
522+
foundShip = ship.id;
523+
shipRelativePos = { x: dx, y: dy }; // Store position relative to ship center
524+
}
525+
});
526+
527+
// Update onShip state
528+
if (foundShip !== this.onShip) {
529+
if (foundShip) {
530+
console.log(`Player boarded ship: ${foundShip}`);
531+
this.onShip = foundShip;
532+
this.shipRelativePosition = shipRelativePos;
533+
} else {
534+
console.log(`Player left ship: ${this.onShip}`);
535+
this.onShip = null;
536+
this.shipRelativePosition = null;
537+
}
538+
} else if (foundShip && shipRelativePos) {
539+
// Update relative position if still on same ship
540+
this.shipRelativePosition = shipRelativePos;
541+
}
542+
}
543+
469544
private checkTileCollision(worldX: number, worldY: number): {
470545
walkable: boolean;
471546
speedModifier: number;

clients/mew-world/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface Player {
6565
lastUpdate: number;
6666
velocity: { x: number; y: number };
6767
platformRef: string | null;
68+
onShip: string | null; // Ship participant ID if player is on a ship
6869
}
6970

7071
/**

0 commit comments

Comments
 (0)