# view.ludic - the window onto the map: a square of `size` pixels at (x0, y0) showing `span` metres # across at zoom 1, centred on a world point. World to window and back, and a scale bar's length. # metres across the window at zoom 1, the zoom's range, and how close a focus goes at least export function minimap_config_view(minimap_st: mut MinimapState, span: float, zoom_min: float, zoom_max: float, focus: float) -> void { minimap_st.mm_span = Math.max(span, 1.0) minimap_st.mm_zoom_min = zoom_min minimap_st.mm_zoom_max = Math.max(zoom_max, zoom_min) minimap_st.mm_zoom_focus = focus minimap_st.mm_zoom = Math.clamp(1.0, minimap_st.mm_zoom_min, minimap_st.mm_zoom_max) minimap_st.mm_have_view = false } # where the window is on the screen this frame export function minimap_window(minimap_st: mut MinimapState, x0: float, y0: float, size: float) -> void { minimap_st.mm_x0 = x0 minimap_st.mm_y0 = y0 minimap_st.mm_size = Math.max(size, 1.0) } export function minimap_size(minimap_st: MinimapState) -> float { return minimap_st.mm_size } export function minimap_zoom(minimap_st: MinimapState) -> float { return minimap_st.mm_zoom } export function minimap_view_x(minimap_st: MinimapState) -> float { return minimap_st.mm_vx } export function minimap_view_z(minimap_st: MinimapState) -> float { return minimap_st.mm_vz } export function minimap_have_view(minimap_st: MinimapState) -> bool { return minimap_st.mm_have_view } # the metres the window shows across at the zoom export function minimap_span(minimap_st: MinimapState) -> float { return minimap_st.mm_span / minimap_st.mm_zoom } # pixels per metre export function minimap_scale(minimap_st: MinimapState) -> float { return minimap_st.mm_size / minimap_span(minimap_st) } # a world point on the screen export function minimap_px(minimap_st: MinimapState, x: float) -> float { return minimap_st.mm_x0 + minimap_st.mm_size * 0.5 + (x - minimap_st.mm_vx) * minimap_scale(minimap_st) } export function minimap_py(minimap_st: MinimapState, z: float) -> float { return minimap_st.mm_y0 + minimap_st.mm_size * 0.5 + (z - minimap_st.mm_vz) * minimap_scale(minimap_st) } # the world under a point of the window (pixels from its corner) export function minimap_wx(minimap_st: MinimapState, rx: float) -> float { return minimap_st.mm_vx + (rx - minimap_st.mm_size * 0.5) / minimap_scale(minimap_st) } export function minimap_wz(minimap_st: MinimapState, ry: float) -> float { return minimap_st.mm_vz + (ry - minimap_st.mm_size * 0.5) / minimap_scale(minimap_st) } # a round distance whose bar is no wider than a quarter of the window export function minimap_scale_metres(minimap_st: MinimapState) -> int { for k in 0 .. 7 { let m = mm_scale_step(k) if float(m) * minimap_scale(minimap_st) <= minimap_st.mm_size / 4.0 { return m } } return 25 } # the view's centre is kept where MinimapWorld allows, so the land can reach the edge but not leave export function minimap_clamp(minimap_st: mut MinimapState) -> void { minimap_st.mm_vx = Math.clamp(minimap_st.mm_vx, MinimapWorld.lo_x(), MinimapWorld.hi_x()) minimap_st.mm_vz = Math.clamp(minimap_st.mm_vz, MinimapWorld.lo_z(), MinimapWorld.hi_z()) } export function minimap_centre(minimap_st: mut MinimapState, x: float, z: float) -> void { minimap_st.mm_vx = x minimap_st.mm_vz = z minimap_st.mm_have_view = true minimap_clamp(minimap_st) } # the view on the camp, or on you export function minimap_recentre(minimap_st: mut MinimapState, on_you: bool) -> void { if on_you { minimap_centre(minimap_st, MinimapWorld.you_x(), MinimapWorld.you_z()) } else { minimap_centre(minimap_st, MinimapWorld.camp_x(), MinimapWorld.camp_z()) } } # go there AND close in; never out, since a player already closer asked for that export function minimap_focus(minimap_st: mut MinimapState, on_you: bool) -> void { minimap_recentre(minimap_st, on_you) if minimap_st.mm_zoom < minimap_st.mm_zoom_focus { minimap_st.mm_zoom = Math.clamp(minimap_st.mm_zoom_focus, minimap_st.mm_zoom_min, minimap_st.mm_zoom_max) } } # the round distances a scale bar can show, largest first function mm_scale_step(k: int) -> int { if k == 0 { return 2000 } if k == 1 { return 1000 } if k == 2 { return 500 } if k == 3 { return 250 } if k == 4 { return 100 } if k == 5 { return 50 } return 25 }