77import net .minecraft .client .player .Input ;
88import net .minecraft .client .player .KeyboardInput ;
99import net .minecraft .network .chat .Component ;
10+ import net .minecraft .world .entity .Entity ;
1011import net .minecraft .world .level .ChunkPos ;
1112import net .xolt .freecam .config .ModConfig ;
13+ import net .xolt .freecam .tripod .TripodRegistry ;
14+ import net .xolt .freecam .tripod .TripodSlot ;
1215import net .xolt .freecam .util .FreeCamera ;
1316import net .xolt .freecam .util .FreecamPosition ;
1417import net .xolt .freecam .variant .api .BuildVariant ;
15- import org .lwjgl .glfw .GLFW ;
16-
17- import java .util .HashMap ;
18+ import org .jetbrains .annotations .Nullable ;
1819
1920import static net .xolt .freecam .config .ModBindings .*;
2021
2122public class Freecam {
2223
2324 public static final Minecraft MC = Minecraft .getInstance ();
2425 public static final String MOD_ID = "freecam" ;
26+ private static final long TOGGLE_KEY_MAX_TICKS = 10 ;
2527
2628 private static boolean freecamEnabled = false ;
2729 private static boolean tripodEnabled = false ;
2830 private static boolean playerControlEnabled = false ;
2931 private static boolean disableNextTick = false ;
30- private static Integer activeTripod = null ;
32+ private static boolean toggleKeyUsedWhileHeld = false ;
33+ private static long toggleKeyHeldTicks = 0 ;
34+ private static final TripodRegistry tripods = new TripodRegistry ();
35+ private static TripodSlot activeTripod = TripodSlot .NONE ;
3136 private static FreeCamera freeCamera ;
32- private static HashMap <Integer , FreecamPosition > overworld_tripods = new HashMap <>();
33- private static HashMap <Integer , FreecamPosition > nether_tripods = new HashMap <>();
34- private static HashMap <Integer , FreecamPosition > end_tripods = new HashMap <>();
3537 private static CameraType rememberedF5 = null ;
3638
3739 public static void preTick (Minecraft mc ) {
3840 if (isEnabled ()) {
3941 // Disable if the previous tick asked us to
40- if (disableNextTick () ) {
42+ if (disableNextTick ) {
4143 toggle ();
42- disableNextTick = false ;
4344 }
4445
4546 // Prevent player from being controlled when freecam is enabled
@@ -51,35 +52,49 @@ public static void preTick(Minecraft mc) {
5152
5253 mc .gameRenderer .setRenderHand (ModConfig .INSTANCE .visual .showHand );
5354 }
55+ disableNextTick = false ;
5456 }
5557
5658 public static void postTick (Minecraft mc ) {
57- if (KEY_TRIPOD_RESET .isPressed ()) {
58- for (KeyMapping hotbarKey : mc .options .keyHotbarSlots ) {
59- while (hotbarKey .consumeClick ()) {
60- resetCamera (hotbarKey .getDefaultKey ().getValue ());
61- while (KEY_TRIPOD_RESET .wasPressed ()) {}
59+ if (KEY_TOGGLE .isDown ()) {
60+ // Count held ticks, so we can toggle on release
61+ toggleKeyHeldTicks ++;
62+ KEY_TOGGLE .reset ();
63+
64+ // Handle <toggle_key>+<hotbar_key> combos
65+ for (KeyMapping combo : mc .options .keyHotbarSlots ) {
66+ while (combo .consumeClick ()) {
67+ toggleTripod (TripodSlot .ofKeyCode (combo .getDefaultKey ().getValue ()));
68+ toggleKeyUsedWhileHeld = true ;
6269 }
6370 }
6471 }
72+ // Check if toggle was pressed, and is now released
73+ else if (KEY_TOGGLE .consumeClick () || toggleKeyHeldTicks > 0 ) {
74+ // Only toggle if the key wasn't used (or held too long)
75+ if (!toggleKeyUsedWhileHeld && toggleKeyHeldTicks < TOGGLE_KEY_MAX_TICKS ) {
76+ toggle ();
77+ }
78+ // Reset state
79+ KEY_TOGGLE .reset ();
80+ toggleKeyHeldTicks = 0 ;
81+ toggleKeyUsedWhileHeld = false ;
82+ }
6583
66- if ( KEY_TOGGLE . isPressed ()) {
67- for ( KeyMapping hotbarKey : mc . options . keyHotbarSlots ) {
68- while ( hotbarKey . consumeClick () ) {
69- toggleTripod ( hotbarKey . getDefaultKey (). getValue ());
70- while ( KEY_TOGGLE . wasPressed ()) {}
84+ // Handle <reset_key>+<hotbar_key> combos
85+ if ( KEY_TRIPOD_RESET . isDown () ) {
86+ for ( KeyMapping key : mc . options . keyHotbarSlots ) {
87+ while ( key . consumeClick ()) {
88+ resetCamera ( TripodSlot . ofKeyCode ( key . getDefaultKey (). getValue ()));
7189 }
7290 }
73- } else if (KEY_TOGGLE .wasPressed ()) {
74- toggle ();
75- while (KEY_TOGGLE .wasPressed ()) {}
7691 }
7792
78- while (KEY_PLAYER_CONTROL .wasPressed ()) {
93+ while (KEY_PLAYER_CONTROL .consumeClick ()) {
7994 switchControls ();
8095 }
8196
82- while (KEY_CONFIG_GUI .wasPressed ()) {
97+ while (KEY_CONFIG_GUI .consumeClick ()) {
8398 mc .setScreen (AutoConfig .getConfigScreen (ModConfig .class , mc .screen ).get ());
8499 }
85100 }
@@ -88,7 +103,7 @@ public static void onDisconnect() {
88103 if (isEnabled ()) {
89104 toggle ();
90105 }
91- clearTripods ();
106+ tripods . clear ();
92107 }
93108
94109 public static void toggle () {
@@ -108,24 +123,24 @@ public static void toggle() {
108123 }
109124 }
110125
111- private static void toggleTripod (Integer keyCode ) {
112- if (keyCode == null ) {
126+ private static void toggleTripod (TripodSlot tripod ) {
127+ if (tripod == TripodSlot . NONE ) {
113128 return ;
114129 }
115130
116131 if (tripodEnabled ) {
117- if (activeTripod . equals ( keyCode ) ) {
132+ if (activeTripod == tripod ) {
118133 onDisableTripod ();
119134 tripodEnabled = false ;
120135 } else {
121136 onDisableTripod ();
122- onEnableTripod (keyCode );
137+ onEnableTripod (tripod );
123138 }
124139 } else {
125140 if (freecamEnabled ) {
126141 toggle ();
127142 }
128- onEnableTripod (keyCode );
143+ onEnableTripod (tripod );
129144 tripodEnabled = true ;
130145 }
131146 if (!tripodEnabled ) {
@@ -147,55 +162,53 @@ public static void switchControls() {
147162 playerControlEnabled = !playerControlEnabled ;
148163 }
149164
150- private static void onEnableTripod (int keyCode ) {
165+ private static void onEnableTripod (TripodSlot tripod ) {
151166 onEnable ();
152167
153- FreecamPosition position = getTripodsForDimension () .get (keyCode );
168+ FreecamPosition position = tripods .get (tripod );
154169 boolean chunkLoaded = false ;
155170 if (position != null ) {
156171 ChunkPos chunkPos = position .getChunkPos ();
157172 chunkLoaded = MC .level .getChunkSource ().hasChunk (chunkPos .x , chunkPos .z );
158173 }
159174
160175 if (!chunkLoaded ) {
161- resetCamera (keyCode );
176+ resetCamera (tripod );
162177 position = null ;
163178 }
164179
180+ freeCamera = new FreeCamera (-420 - tripod .ordinal ());
165181 if (position == null ) {
166- freeCamera = new FreeCamera (- 420 - ( keyCode % GLFW . GLFW_KEY_0 ) );
182+ moveToPlayer ( );
167183 } else {
168- freeCamera = new FreeCamera (- 420 - ( keyCode % GLFW . GLFW_KEY_0 ), position );
184+ moveToPosition ( position );
169185 }
170186
171187 freeCamera .spawn ();
172188 MC .setCameraEntity (freeCamera );
173- activeTripod = keyCode ;
189+ activeTripod = tripod ;
174190
175191 if (ModConfig .INSTANCE .notification .notifyTripod ) {
176- MC .player .displayClientMessage (Component .translatable ("msg.freecam.openTripod" ). append ( "" + activeTripod % GLFW . GLFW_KEY_0 ), true );
192+ MC .player .displayClientMessage (Component .translatable ("msg.freecam.openTripod" , tripod ), true );
177193 }
178194 }
179195
180196 private static void onDisableTripod () {
181- getTripodsForDimension () .put (activeTripod , new FreecamPosition (freeCamera ));
197+ tripods .put (activeTripod , new FreecamPosition (freeCamera ));
182198 onDisable ();
183199
184200 if (MC .player != null ) {
185201 if (ModConfig .INSTANCE .notification .notifyTripod ) {
186- MC .player .displayClientMessage (Component .translatable ("msg.freecam.closeTripod" ). append ( "" + activeTripod % GLFW . GLFW_KEY_0 ), true );
202+ MC .player .displayClientMessage (Component .translatable ("msg.freecam.closeTripod" , activeTripod ), true );
187203 }
188204 }
189- activeTripod = null ;
205+ activeTripod = TripodSlot . NONE ;
190206 }
191207
192208 private static void onEnableFreecam () {
193209 onEnable ();
194210 freeCamera = new FreeCamera (-420 );
195- freeCamera .applyPerspective (
196- ModConfig .INSTANCE .visual .perspective ,
197- ModConfig .INSTANCE .collision .alwaysCheck || !(ModConfig .INSTANCE .collision .ignoreAll && BuildVariant .getInstance ().cheatsPermitted ())
198- );
211+ moveToPlayer ();
199212 freeCamera .spawn ();
200213 MC .setCameraEntity (freeCamera );
201214
@@ -244,50 +257,57 @@ private static void onDisabled() {
244257 }
245258 }
246259
247- private static void resetCamera (int keyCode ) {
248- if (tripodEnabled && activeTripod != null && activeTripod == keyCode && freeCamera != null ) {
249- freeCamera . copyPosition ( MC . player );
260+ private static void resetCamera (TripodSlot tripod ) {
261+ if (tripodEnabled && activeTripod != TripodSlot . NONE && activeTripod == tripod && freeCamera != null ) {
262+ moveToPlayer ( );
250263 } else {
251- getTripodsForDimension () .put (keyCode , null );
264+ tripods .put (tripod , null );
252265 }
253266
254267 if (ModConfig .INSTANCE .notification .notifyTripod ) {
255- MC .player .displayClientMessage (Component .translatable ("msg.freecam.tripodReset" ). append ( "" + keyCode % GLFW . GLFW_KEY_0 ), true );
268+ MC .player .displayClientMessage (Component .translatable ("msg.freecam.tripodReset" , tripod ), true );
256269 }
257270 }
258271
259- public static void clearTripods () {
260- overworld_tripods = new HashMap <>();
261- nether_tripods = new HashMap <>();
262- end_tripods = new HashMap <>();
272+ public static void moveToEntity (@ Nullable Entity entity ) {
273+ if (freeCamera == null ) {
274+ return ;
275+ }
276+ if (entity == null ) {
277+ moveToPlayer ();
278+ return ;
279+ }
280+ freeCamera .copyPosition (entity );
263281 }
264282
265- public static FreeCamera getFreeCamera () {
266- return freeCamera ;
283+ public static void moveToPosition (@ Nullable FreecamPosition position ) {
284+ if (freeCamera == null ) {
285+ return ;
286+ }
287+ if (position == null ) {
288+ moveToPlayer ();
289+ return ;
290+ }
291+ freeCamera .applyPosition (position );
267292 }
268293
269- public static HashMap <Integer , FreecamPosition > getTripodsForDimension () {
270- HashMap <Integer , FreecamPosition > result ;
271- switch (MC .level .dimensionTypeId ().location ().getPath ()) {
272- case "the_nether" :
273- result = nether_tripods ;
274- break ;
275- case "the_end" :
276- result = end_tripods ;
277- break ;
278- default :
279- result = overworld_tripods ;
280- break ;
281- }
282- return result ;
294+ public static void moveToPlayer () {
295+ if (freeCamera == null ) {
296+ return ;
297+ }
298+ freeCamera .copyPosition (MC .player );
299+ freeCamera .applyPerspective (
300+ ModConfig .INSTANCE .visual .perspective ,
301+ ModConfig .INSTANCE .collision .alwaysCheck || !(ModConfig .INSTANCE .collision .ignoreAll && BuildVariant .getInstance ().cheatsPermitted ())
302+ );
283303 }
284304
285- public static boolean disableNextTick () {
286- return disableNextTick ;
305+ public static FreeCamera getFreeCamera () {
306+ return freeCamera ;
287307 }
288308
289- public static void setDisableNextTick ( boolean damage ) {
290- disableNextTick = damage ;
309+ public static void disableNextTick ( ) {
310+ disableNextTick = true ;
291311 }
292312
293313 public static boolean isEnabled () {
0 commit comments