RuneHive-Game
Loading...
Searching...
No Matches
DialogueCameraDirector.java
Go to the documentation of this file.
1package com.runehive.game.world.entity.mob.player.camera;
2
3import com.runehive.game.world.entity.mob.player.Player;
4import com.runehive.game.world.position.Position;
5import com.runehive.game.world.entity.mob.npc.Npc;
6import com.runehive.net.packet.out.SendCameraMove;
7import com.runehive.net.packet.out.SendCameraTurn;
8import com.runehive.net.packet.out.SendCameraReset;
9
10/**
11 * Drives a simple "over-the-shoulder" dialogue camera:
12 * - BEHIND_PLAYER: camera behind player, looks at NPC
13 * - BEHIND_NPC: camera behind NPC, looks at player
14 * Smoothness is controlled by camera packet speeds (const/var).
15 */
16public final class DialogueCameraDirector {
17
19
20 // Enable debug logging (set to false for production)
21 private static final boolean DEBUG = true;
22
23 // --- Shoulder/eye-line tuning ---
24 private static final double BACK = 1.7; // tiles behind current speaker
25 private static final double RIGHT_BASE = 0.35; // subtle shoulder offset baseline
26 private static final double RIGHT_NUDGE= 0.25; // 0.35 + 0.25 = 0.60 total
27 private static final double RIGHT = RIGHT_BASE + RIGHT_NUDGE; // = 0.60 effective
28 private static final int MOVE_Z = 220; // eye/shoulder-ish height for 166
29 private static final int TURN_TILT_Z = 210; // comfortable tilt for 177
30
31 // --- Base smooth speeds (constant speed, no distance-based slowdown) ---
32 private static final int MOVE_CONST_BASE = 18, MOVE_VAR_BASE = 0;
33 private static final int TURN_CONST_BASE = 20, TURN_VAR_BASE = 0;
34 // --- Slightly faster turn during swaps (constant speed) ---
35 private static final int MOVE_CONST_SWAP = 16, MOVE_VAR_SWAP = 0;
36 private static final int TURN_CONST_SWAP = 14, TURN_VAR_SWAP = 0;
37
39
40 /** Recompute and send camera on each tick while active. */
41 public static void tick(Player p) {
42 if (p.dialogueCamMode == Mode.OFF) return;
43 if (p.regionChange) return; // skip 1-2 ticks while scene base shifts
44
45 // Handle delay before camera activation
46 if (p.dialogueCamDelayTicks > 0) {
48 if (p.dialogueCamDelayTicks == 0) {
49 p.dialogueCamMode = Mode.BEHIND_PLAYER; // now safe to activate
50 if (DEBUG) {
51 System.out.println("[DialogueCam] Delay complete, activating BEHIND_PLAYER mode");
52 }
53 }
54 if (DEBUG) {
55 System.out.println("[DialogueCam] Delaying activation: " + p.dialogueCamDelayTicks + " ticks remaining");
56 }
57 return;
58 }
59
60 if (p.dialogueCamMode == Mode.OFF) return;
61
62 final Npc npc = p.dialogueCamNpc;
63 if (npc == null || npc.isDead() || p.isDead()) {
64 // If target is gone, reset smoothly
65 requestReset(p);
66 return;
67 }
68
69 final Position playerPos = p.getPosition();
70 final Position npcPos = npc.getPosition();
71
72 // Choose speeds; during swap-boost we turn a bit faster
73 final boolean swapBoost = p.dialogueCamSwapBoostTicks > 0;
74 final int moveConst = swapBoost ? MOVE_CONST_SWAP : MOVE_CONST_BASE;
75 final int moveVar = swapBoost ? MOVE_VAR_SWAP : MOVE_VAR_BASE;
76 final int turnConst = swapBoost ? TURN_CONST_SWAP : TURN_CONST_BASE;
77 final int turnVar = swapBoost ? TURN_VAR_SWAP : TURN_VAR_BASE;
78
79 if (DEBUG) {
80 System.out.println("[DialogueCam] Mode=" + p.dialogueCamMode +
81 " | Delay=" + p.dialogueCamDelayTicks +
82 " | PlayerPos=" + p.getPosition() +
83 " | NpcPos=" + (p.dialogueCamNpc != null ? p.dialogueCamNpc.getPosition() : "null"));
84 }
85
86 switch (p.dialogueCamMode) {
87 case BEHIND_PLAYER -> {
88 shoulderShot(p, playerPos, npcPos, moveConst, moveVar, turnConst, turnVar); // behind player, look at NPC
89 }
90 case BEHIND_NPC -> {
91 shoulderShot(p, npcPos, playerPos, moveConst, moveVar, turnConst, turnVar); // behind NPC, look at player
92 }
93 case RESET_PENDING -> {
94 // Send one gentle easing frame, then clear cinematics
95 shoulderShot(p, playerPos, playerPos, MOVE_CONST_BASE, MOVE_VAR_BASE, TURN_CONST_BASE, TURN_VAR_BASE); // short ease toward default
96 p.send(new SendCameraReset()); // opcode 107
97 p.dialogueCamMode = Mode.OFF;
98 p.dialogueCamNpc = null;
99 p.dialogueCamSwapBoostTicks = 0;
100 }
101 default -> { /* no-op */ }
102 }
103
104 if (p.dialogueCamSwapBoostTicks > 0) {
106 }
107 }
108
109 /** Place camera behind 'from' and turn toward 'to'. */
110 private static void shoulderShot(Player p, Position from, Position to,
111 int moveConst, int moveVar, int turnConst, int turnVar) {
112 System.out.println("== Dialogue Camera Shot ==");
113 System.out.println("Player world pos : " + from);
114 System.out.println("Target world pos : " + to);
115 System.out.println("Player position : " + p.getPosition());
116 System.out.println("from.getLocalX(p.pos) : " + from.getLocalX(p.getPosition()));
117 System.out.println("from.getLocalY(p.pos) : " + from.getLocalY(p.getPosition()));
118 System.out.println("to.getLocalX(p.pos) : " + to.getLocalX(p.getPosition()));
119 System.out.println("to.getLocalY(p.pos) : " + to.getLocalY(p.getPosition()));
120
121 final double dx = to.getX() - from.getX();
122 final double dy = to.getY() - from.getY();
123 final double len = Math.max(1e-6, Math.hypot(dx, dy));
124
125 // Forward vector from 'from' → 'to'
126 final double fx = dx / len;
127 final double fy = dy / len;
128
129 // Right-hand vector (perpendicular on the plane)
130 final double rx = fy, ry = -fx;
131
132 // Behind-right shoulder camera position
133 final double camX = from.getX() - fx * BACK + rx * RIGHT;
134 final double camY = from.getY() - fy * BACK + ry * RIGHT;
135
136 final Position cam = new Position((int)Math.round(camX), (int)Math.round(camY), from.getHeight());
137 final Position look = to; // look straight at the target speaker/listener
138
139 // Guard: skip if we haven't set the scene base yet (first tick before MapRegion)
140 if (p.getSceneBaseChunkX() == 0 && p.getSceneBaseChunkY() == 0) {
141 return;
142 }
143
144 final int localCamX = p.toSceneLocalX(cam);
145 final int localCamY = p.toSceneLocalY(cam);
146 final int localLookX = p.toSceneLocalX(look);
147 final int localLookY = p.toSceneLocalY(look);
148
149 // Safety bounds
150 if ((localCamX|localCamY|localLookX|localLookY) < 0 ||
151 localCamX > 104 || localCamY > 104 || localLookX > 104 || localLookY > 104) {
152 System.err.printf("[Camera BUG] Out-of-bounds: LocalCam=(%d,%d) LocalLook=(%d,%d)%n",
153 localCamX, localCamY, localLookX, localLookY);
154 return;
155 }
156
157 if (DEBUG) {
158 System.out.printf("GlobalCam=(%d,%d) GlobalLook=(%d,%d) | SceneBaseTiles=(%d,%d) | LocalCam=(%d,%d) LocalLook=(%d,%d)%n",
159 cam.getX(), cam.getY(),
160 look.getX(), look.getY(),
161 p.getSceneBaseChunkX() * 8, p.getSceneBaseChunkY() * 8,
162 localCamX, localCamY,
163 localLookX, localLookY
164 );
165 }
166
167 p.send(new SendCameraMove(localCamX, localCamY, MOVE_Z, moveConst, moveVar));
168 p.send(new SendCameraTurn(localLookX, localLookY, TURN_TILT_Z, turnConst, turnVar));
169 }
170
171 /** Ask for a smooth reset on the next tick. */
172 public static void requestReset(Player p) {
173 if (p.dialogueCamMode != Mode.OFF) {
174 p.dialogueCamMode = Mode.RESET_PENDING;
175 }
176 }
177
178 /** Small helper to prime faster turn for a few ticks after a mode swap. */
179 public static void boostSwap(Player p, int ticks) {
180 p.dialogueCamSwapBoostTicks = Math.max(p.dialogueCamSwapBoostTicks, Math.max(1, ticks));
181 }
182
183}
Represents a non-player character in the in-game world.
Definition Npc.java:29
This class represents a character controlled by a player.
Definition Player.java:125
transient int dialogueCamDelayTicks
Delay ticks before camera activates (prevents coordinate misalignment at dialogue start).
Definition Player.java:174
transient int dialogueCamSwapBoostTicks
While >0, camera turns use slightly faster "swap" speeds for a smoother P↔NPC flip.
Definition Player.java:172
transient com.runehive.game.world.entity.mob.player.camera.DialogueCameraDirector.Mode dialogueCamMode
Dialogue camera system.
Definition Player.java:169
transient com.runehive.game.world.entity.mob.npc.Npc dialogueCamNpc
Definition Player.java:170
static void requestReset(Player p)
Ask for a smooth reset on the next tick.
static void tick(Player p)
Recompute and send camera on each tick while active.
static void shoulderShot(Player p, Position from, Position to, int moveConst, int moveVar, int turnConst, int turnVar)
Place camera behind 'from' and turn toward 'to'.
static void boostSwap(Player p, int ticks)
Small helper to prime faster turn for a few ticks after a mode swap.
Represents a single tile on the game world.
Definition Position.java:14
int getHeight()
Gets the height coordinate, or height.
Definition Position.java:51
int getY()
Gets the absolute y coordinate.
Definition Position.java:46
int getX()
Gets the absolute x coordinate.
Definition Position.java:41
int getLocalY()
Gets the local y coordinate relative to this region.
Definition Position.java:61
int getLocalX()
Gets the local x coordinate relative to this region.
Definition Position.java:56
The OutgoingPacket resets the camera position for Player.