33#include " mon_feature.hpp"
44
55/*
6- * TODO feedback from bill
7- *
8- help text for newlocation cmd - you have to actually name the portals
9-
10- future:
11- toggle for showing player outline within portal bounds
12- set_name manually
6+ * TODO future features:
7+ * - toggle for showing where in the portal the player will fit
8+ * - make a version of the newlocation cmd which doesn't require the player to manually name the portals
9+ * - compare the monocle portal values to the game to auto-detect which portal is "placed last"
1310*/
1411
1512#ifdef SPT_MONOCLE_FEATURE
@@ -22,14 +19,15 @@ set_name manually
2219#include " spt\features\tracing.hpp"
2320#include " spt\features\visualizations\imgui\imgui_interface.hpp"
2421
22+ // dumb botched class for a setting which has custom options and an autodetect
2523template <typename T, size_t N>
2624class ComboWithAutoOpt
2725{
2826 static_assert (N > 0 );
2927
3028 std::string autoOptLabel;
3129 std::array<std::pair<T, const char *>, N> userOpts;
32- int imguiOpt = 0 ;
30+ int imguiOpt = 0 ; // 0 is auto
3331 std::optional<T> lastUpdate;
3432 T lastGrabbedVal;
3533
@@ -144,6 +142,43 @@ struct ImGuiPersist
144142
145143} imguiPersist;
146144
145+ void MonocleFeature::UserPixelSelect::Update (const MonocleWorker& wd, WorkerPixel pxl, bool bluePlacedLast_)
146+ {
147+ dirty = false ;
148+ bluePlacedLast = bluePlacedLast_;
149+ selectedPxl = pxl;
150+ imgSize = wd.GetImageSize ();
151+ params = wd.GetMonocleData ()->paramsTemplate ;
152+ params.record_flags = mon::TCRF_RECORD_ENTITY;
153+ params.n_max_teleports = MAX_EDICTS; // I think anything above this is guaranteed to crash
154+ wd.DoWorkForPixel (pxl, params, result);
155+ }
156+
157+ void MonocleFeature::UserPixelSelect::DrawInfo ()
158+ {
159+ ImGui::Text (" pixel: (%hu,%hu) / (%hu,%hu)" , selectedPxl.x , selectedPxl.y , imgSize.x , imgSize.y );
160+ Assert ((params.record_flags & mon::TCRF_RECORD_ENTITY) && !result.ents .empty ());
161+ mon::Vector startCenter = result.ents [0 ].GetCenter ();
162+ ImGui::Text (" entity center: " V_FMT, V_UNP (startCenter));
163+
164+ if (result.max_tps_exceeded )
165+ {
166+ ImGui::Text (" result: unknown (possible crash)" , result.total_n_teleports );
167+ ImGui::Text (" total teleports: unknown (more than %u)" , params.n_max_teleports );
168+ ImGui::Text (" end behind portal: N/A" );
169+ }
170+ else
171+ {
172+ ImGui::Text (" result: %d CUM teleport(s)" , result.cum_teleports );
173+ ImGui::Text (" total teleports: %u" , result.total_n_teleports );
174+ std::optional<bool > endBehind = MonocleWorker::EndBehindPortal (params, result);
175+ if (endBehind.has_value ())
176+ ImGui::Text (" end behind portal: %s" , *endBehind ? " true" : " false" );
177+ else
178+ ImGui::Text (" end behind portal: N/A" );
179+ }
180+ }
181+
147182// callbacks for imgui to disable/re-enable image interpolation for the zoomed in tooltip
148183struct SamplerState
149184{
@@ -182,43 +217,7 @@ struct SamplerState
182217 }
183218};
184219
185- void MonocleFeature::UserPixelSel::Update (const MonocleWorker& wd, WorkerPixel pxl)
186- {
187- active = true ;
188- selectedPxl = pxl;
189- imgSize = wd.GetImageSize ();
190- params = wd.GetMonocleData ()->paramsTemplate ;
191- params.record_flags = mon::TCRF_RECORD_ENTITY;
192- params.n_max_teleports = MAX_EDICTS; // I think anything above this is guaranteed to crash
193- wd.DoWorkForPixel (pxl, params, result);
194- }
195-
196- void MonocleFeature::UserPixelSel::Draw ()
197- {
198- ImGui::Text (" pixel: (%hu,%hu) / (%hu,%hu)" , selectedPxl.x , selectedPxl.y , imgSize.x , imgSize.y );
199- Assert ((params.record_flags & mon::TCRF_RECORD_ENTITY) && !result.ents .empty ());
200- mon::Vector startCenter = result.ents [0 ].GetCenter ();
201- ImGui::Text (" entity center: " V_FMT, V_UNP (startCenter));
202-
203- if (result.max_tps_exceeded )
204- {
205- ImGui::Text (" result: unknown (possible crash)" , result.total_n_teleports );
206- ImGui::Text (" total teleports: unknown (more than %u)" , params.n_max_teleports );
207- ImGui::Text (" end behind portal: N/A" );
208- }
209- else
210- {
211- ImGui::Text (" result: %d CUM teleport(s)" , result.cum_teleports );
212- ImGui::Text (" total teleports: %u" , result.total_n_teleports );
213- std::optional<bool > endBehind = MonocleWorker::EndBehindPortal (params, result);
214- if (endBehind.has_value ())
215- ImGui::Text (" end behind portal: %s" , *endBehind ? " true" : " false" );
216- else
217- ImGui::Text (" end behind portal: N/A" );
218- }
219- }
220-
221- std::optional<WorkerPixel> MonocleFeature::DrawWorkerImage (const char * label, MonocleWorker& wd)
220+ void MonocleFeature::DrawWorkerImage (const char * label, MonocleWorker& wd)
222221{
223222 ImGui::PushID (label);
224223 ImGui::BeginGroup ();
@@ -232,6 +231,13 @@ std::optional<WorkerPixel> MonocleFeature::DrawWorkerImage(const char* label, Mo
232231 ImGui::BeginDisabled (!md);
233232 if (ImGui::Button (" Copy newlocation cmd" ))
234233 ImGui::SetClipboardText (worker1->GetMonocleData ()->pp .NewLocationCmd (" ; " ).c_str ());
234+ if (md)
235+ {
236+ ImGui::SetItemTooltip (
237+ " To use this command, you must manually name your portals 'blue' and 'orange'\n "
238+ " using e.g. 'picker', looking at the portal bubble, and using 'ent_setname blue'." ,
239+ md->paramsTemplate .first_tp_from_blue );
240+ }
235241 imguiPersist.minImgWidth = (int )ImGui::GetItemRectSize ().x ;
236242 ImGui::EndDisabled ();
237243
@@ -315,25 +321,59 @@ std::optional<WorkerPixel> MonocleFeature::DrawWorkerImage(const char* label, Mo
315321 // border
316322 dltt->AddRect (c0, c1, borderColor);
317323
318- if (hoveredPxl.selectedPxl != pxlSelectCoord
319- || hoveredPxl.params .first_tp_from_blue != md->paramsTemplate .first_tp_from_blue )
324+ bool curBluePlacedLast =
325+ wd.GetMonocleData ()->pp .order == mon::PlacementOrder::ORANGE_OPEN_BLUE_NEW_LOCATION;
326+ if (hoveredPxl.dirty || hoveredPxl.selectedPxl != pxlSelectCoord
327+ || hoveredPxl.bluePlacedLast != curBluePlacedLast)
320328 {
321- hoveredPxl.Update (wd, pxlSelectCoord);
329+ hoveredPxl.Update (wd, pxlSelectCoord, curBluePlacedLast );
322330 }
323331
324332 ImGui::SameLine ();
325333 ImGui::BeginGroup ();
326- hoveredPxl.Draw ();
334+ hoveredPxl.DrawInfo ();
327335 ImGui::EndGroup ();
328336
329- ImGui::Text (" Click to get more information about this point" );
337+ ImGui::Text (" Click to copy setpos cmd" );
338+
339+ // warning message
340+ static std::string setposWarningMsg;
341+ setposWarningMsg.clear ();
342+ if (!hoveredPxl.result .ent .is_player )
343+ setposWarningMsg += " - the simulation was run on a non-player entity\n " ;
344+ auto & combos = imguiPersist.combos ;
345+ if (!combos.playerCrouched .LastGrabbedMatchesLastUpdate ())
346+ {
347+ setposWarningMsg += combos.playerCrouched .GetLastUpdateVal () ? " - the player is crouched\n "
348+ : " - the player is not crouched\n " ;
349+ }
350+ if (!combos.mapOriginEmpty .LastGrabbedMatchesLastUpdate ())
351+ {
352+ setposWarningMsg += combos.mapOriginEmpty .GetLastUpdateVal ()
353+ ? " - the map origin is a passable space\n "
354+ : " - the map origin is not a passable space\n " ;
355+ }
356+ if (!combos.monocleGameVersion .LastGrabbedMatchesLastUpdate ())
357+ setposWarningMsg += " - the simulation was done with a different game version\n " ;
358+ if (!setposWarningMsg.empty ())
359+ {
360+ ImGui::TextColored (SPT_IMGUI_WARN_COLOR_YELLOW,
361+ ICON_CI_WARNING " the setpos command may not work as expected because:" );
362+ ImGui::Text (" %.*s" , setposWarningMsg.size () - 1 , setposWarningMsg.c_str ());
363+ }
364+
365+ if (clicked)
366+ {
367+ mon::Entity entInFront = hoveredPxl.result .ents [0 ].WithNewCenter (
368+ hoveredPxl.result .ents [0 ].GetCenter () + hoveredPxl.params .EntryPortal ().f );
369+ std::string cmd = std::format (" {}; spt_afterticks 10 \" {}\" " ,
370+ entInFront.SetPosCmd (),
371+ hoveredPxl.result .ents [0 ].SetPosCmd ());
372+ ImGui::SetClipboardText (cmd.c_str ());
373+ }
330374
331375 ImGui::EndTooltip ();
332376 }
333- else
334- {
335- hoveredPxl.Clear ();
336- }
337377
338378 if (hasTooltip)
339379 {
@@ -346,10 +386,6 @@ std::optional<WorkerPixel> MonocleFeature::DrawWorkerImage(const char* label, Mo
346386
347387 ImGui::EndGroup ();
348388 ImGui::PopID ();
349-
350- if (clicked && md)
351- return pxlSelectCoord;
352- return std::nullopt ;
353389}
354390
355391void MonocleFeature::ImGuiDrawImages ()
@@ -372,17 +408,13 @@ void MonocleFeature::ImGuiDrawImages()
372408
373409 ImGui::BeginGroup ();
374410
375- auto newSelect = DrawWorkerImage (" Blue placed last" , *worker1);
376- if (newSelect.has_value ())
377- selectedPxl.Update (*worker1, newSelect.value ());
411+ DrawWorkerImage (" Blue placed last" , *worker1);
378412
379413 ImGui::SameLine ();
380414 ImGui::Dummy (ImVec2 (10 , 0 ));
381415 ImGui::SameLine ();
382416
383- newSelect = DrawWorkerImage (" Orange placed last" , *worker2);
384- if (newSelect.has_value ())
385- selectedPxl.Update (*worker2, newSelect.value ());
417+ DrawWorkerImage (" Orange placed last" , *worker2);
386418
387419 ImGui::EndGroup ();
388420}
@@ -466,52 +498,6 @@ void MonocleFeature::ImGuiTabCallback()
466498 RestartWorkers (newEntryPortal);
467499
468500 ImGuiDrawImages ();
469-
470- if (selectedPxl.active )
471- {
472- ImGui::SeparatorText (" Selected pixel" );
473-
474- selectedPxl.Draw ();
475-
476- auto & result = selectedPxl.result ;
477- if (ImGui::Button (" Copy setpos cmd" ))
478- {
479- mon::Entity entInFront = result.ents [0 ].WithNewCenter (result.ents [0 ].GetCenter ()
480- + selectedPxl.params .EntryPortal ().f );
481- std::string cmd = std::format (" {}; spt_afterticks 10 \" {}\" " ,
482- entInFront.SetPosCmd (),
483- result.ents [0 ].SetPosCmd ());
484- ImGui::SetClipboardText (cmd.c_str ());
485- }
486- // warning message
487- static std::string setposWarningMsg;
488- setposWarningMsg.clear ();
489- if (!result.ent .is_player )
490- setposWarningMsg += " - The simulation was run on a non-player entity.\n " ;
491- auto & combos = imguiPersist.combos ;
492- if (!combos.playerCrouched .LastGrabbedMatchesLastUpdate ())
493- {
494- setposWarningMsg += combos.playerCrouched .GetLastUpdateVal ()
495- ? " - The player is crouched.\n "
496- : " - The player is not crouched.\n " ;
497- }
498- if (!combos.mapOriginEmpty .LastGrabbedMatchesLastUpdate ())
499- {
500- setposWarningMsg += combos.mapOriginEmpty .GetLastUpdateVal ()
501- ? " - The map origin is a passable space.\n "
502- : " - The map origin is not a passable space.\n " ;
503- }
504- if (!combos.monocleGameVersion .LastGrabbedMatchesLastUpdate ())
505- setposWarningMsg += " - The simulation was done with a different game version.\n " ;
506- if (!setposWarningMsg.empty ())
507- {
508- ImGui::SameLine ();
509- ImGui::TextColored (SPT_IMGUI_WARN_COLOR_RED, ICON_CI_WARNING);
510- ImGui::SetItemTooltip (" This command may not work as expected because:\n %.*s" ,
511- setposWarningMsg.size () - 1 ,
512- setposWarningMsg.c_str ());
513- }
514- }
515501}
516502
517503bool MonocleFeature::ImGuiDrawSettings ()
@@ -643,10 +629,6 @@ void MonocleFeature::RestartWorkers(const utils::PortalInfo* newEntryPortal)
643629 imguiPersist.combos .monocleGameVersion .Grab (),
644630 paramsTemplate);
645631 }
646- else
647- {
648- selectedPxl.Clear ();
649- }
650632
651633 if (!worker1)
652634 worker1 = std::make_shared<MonocleWorker>();
@@ -656,16 +638,7 @@ void MonocleFeature::RestartWorkers(const utils::PortalInfo* newEntryPortal)
656638 worker1->RestartWork (std::move (newMonData1), {w, h});
657639 worker2->RestartWork (std::move (newMonData2), {w, h});
658640
659- if (hasOpenPortals && selectedPxl.active && worker1)
660- {
661- // adjust user pixel selection so that it's in the same relative spot in the image
662- WorkerPixel oldImgSize = worker1->GetImageSize ();
663- WorkerPixel oldPxlSelect = selectedPxl.selectedPxl ;
664- MonocleWorker& wm = selectedPxl.params .first_tp_from_blue ? *worker1 : *worker2;
665- selectedPxl.Update (wm,
666- {(uint16_t )((float )oldPxlSelect.x / oldImgSize.x * w),
667- (uint16_t )((float )oldPxlSelect.y / oldImgSize.y * h)});
668- }
641+ hoveredPxl.dirty = true ;
669642}
670643
671644void MonocleFeature::OnTickSignal (bool )
@@ -716,8 +689,6 @@ void MonocleFeature::UnloadFeature()
716689{
717690 worker1.reset ();
718691 worker2.reset ();
719- selectedPxl.Clear ();
720- hoveredPxl.Clear ();
721692}
722693
723694#endif
0 commit comments