From e42b2ed66bfdf2c5f6468c69846f2663e41971ef Mon Sep 17 00:00:00 2001 From: RallyBlock Date: Mon, 9 Nov 2026 20:45:56 -0500 Subject: [PATCH] fix camera tick: CamTickOn on HUD; wheel zoom --- README.md | 36 +- client.cs | 2345 ++++++++++++++++++++++++----------------------- description.txt | 4 +- ui.cs | 144 +-- 4 files changed, 1296 insertions(+), 1233 deletions(-) diff --git a/README.md b/README.md index f0de291..ff6cd16 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ -# Client_RPG - -Required client add-on for **GameMode_RPG**: top-down orbit camera, click-to-move/combat, HUD and `/rpg` window hotkeys. - -Install into `Add-Ons/Client_RPG` (same folder name as on the server). - -## Controls - -| Input | Action | -|---|---| -| Left click | Walk / attack / interact (server resolves the ray) | -| Right click | Talk / context | -| A/D, arrows | Rotate camera | -| PgUp/PgDn, +/- | Zoom | -| B / K / E | Bag / skills / equip lists | -| R | Toggle auto-retaliate | - -Protocol version **2** — must match `GameMode_RPG` ui/protocol.cs. +# Client_RPG + +Required client add-on for **GameMode_RPG**: top-down orbit camera, click-to-move/combat, HUD and `/rpg` window hotkeys. + +Install into `Add-Ons/Client_RPG` (same folder name as on the server). + +## Controls + +| Input | Action | +|---|---| +| Left click | Walk / attack / interact (server resolves the ray) | +| Right click | Talk / context | +| A/D, arrows | Rotate camera | +| PgUp/PgDn, +/- | Zoom | +| B / K / E | Bag / skills / equip lists | +| R | Toggle auto-retaliate | + +Protocol version **2** — must match `GameMode_RPG` ui/protocol.cs. diff --git a/client.cs b/client.cs index 8776262..302f65f 100644 --- a/client.cs +++ b/client.cs @@ -1,1141 +1,1204 @@ -// Client_RPG - top-down camera, click-to-move, and RPG windows for GameMode_RPG. -exec("./ui.cs"); -// The server probes with RPG_Probe; reply with serverCmdRPG_Hello(2) to play. - -$RPGClient::Version = 2; -$RPGClient::Active = 0; -$RPGClient::Yaw = 0; -$RPGClient::Dist = 14; -$RPGClient::TopDownPitch = 1.22; -$RPGClient::OrbitInit = 0; -$RPGClient::HudPlaced = 0; -$RPGClient::KeyMapPushed = 0; -$RPGClient::MoveHijacked = 0; -$RPGClient::GlobalKeys = 0; -$RPGClient::PlayBooted = 0; -$RPGClient::PanX = 0; -$RPGClient::PanY = 0; -$RPGClient::PanMax = 48; -$RPGClient::YawSpeed = 0.045; -$RPGClient::ZoomSpeed = 0.12; -$RPGClient::CamTickSched = 0; -$RPGClient::LastCamSync = 0; -$RPGClient::MvF = 0; -$RPGClient::MvB = 0; -$RPGClient::MvL = 0; -$RPGClient::MvR = 0; -$RPGClient::CamModeReady = 0; -$RPGClient::HelloPending = 0; -$RPGClient::HelloLast = ""; -$RPGClient::TopDownWatchSched = 0; -$RPGClient::CamSched = 0; -$RPGClient::LastClickTime = 0; - -function RPGClient_NormPos(%ctrl, %pos) -{ - if (!isObject(%ctrl)) - { - return "0.5 0.5"; - } - %ext = %ctrl.getExtent(); - %w = getWord(%ext, 0); - %h = getWord(%ext, 1); - if (%w < 1) - { - %w = 1; - } - if (%h < 1) - { - %h = 1; - } - %x = getWord(%pos, 0) / %w; - %y = 1 - getWord(%pos, 1) / %h; - if (%x < 0) - { - %x = 0; - } - if (%x > 1) - { - %x = 1; - } - if (%y < 0) - { - %y = 0; - } - if (%y > 1) - { - %y = 1; - } - return %x SPC %y; -} - -function RPGClient_Aspect() -{ - if (!isObject(Canvas)) - { - return 1.333; - } - %ext = Canvas.getExtent(); - %w = getWord(%ext, 0); - %h = getWord(%ext, 1); - return %h > 0 ? %w / %h : 1.333; -} - -function RPGClient_SendClick(%button, %pos) -{ - if (!isObject(ServerConnection)) - { - return; - } - %now = getSimTime(); - if (%now - $RPGClient::LastClickTime < 80) - { - return; - } - $RPGClient::LastClickTime = %now; - %norm = RPGClient_NormPos(RPGClient_Mouse, %pos); - commandToServer('RPG_Click', %button, getWord(%norm, 0), getWord(%norm, 1), RPGClient_Aspect()); - schedule(1, 0, RPGClient_ApplyCamera, 1); -} - -function RPGClient_Mouse::onMouseDown(%this, %mod, %pos, %clicks) -{ - if (!$RPGClient::Active) - { - return; - } - RPGClient_SendClick(0, %pos); -} - -function RPGClient_Mouse::onRightMouseDown(%this, %mod, %pos, %clicks) -{ - if (!$RPGClient::Active) - { - return; - } - RPGClient_SendClick(1, %pos); -} - -function RPGClient_Mouse::onWake(%this) -{ - if (isObject(Canvas)) - { - %ext = Canvas.getExtent(); - %this.resize(0, 0, getWord(%ext, 0), getWord(%ext, 1)); - } -} - -function RPGClient_ClampPan() -{ - %max = $RPGClient::PanMax; - if ($RPGClient::PanX > %max) - { - $RPGClient::PanX = %max; - } - if ($RPGClient::PanX < -%max) - { - $RPGClient::PanX = -%max; - } - if ($RPGClient::PanY > %max) - { - $RPGClient::PanY = %max; - } - if ($RPGClient::PanY < -%max) - { - $RPGClient::PanY = -%max; - } -} - -function RPGClient_ViewTransform(%body, %yaw, %dist, %panX, %panY) -{ - %p = %body.getPosition(); - %cx = getWord(%p, 0) + %panX; - %cy = getWord(%p, 1) + %panY; - %cz = getWord(%p, 2) + 1.5; - %pitch = $RPGClient::TopDownPitch; - %bx = -mCos(%yaw) * mCos(%pitch) * %dist; - %by = -mSin(%yaw) * mCos(%pitch) * %dist; - %bz = mSin(%pitch) * %dist; - %ex = %cx + %bx; - %ey = %cy + %by; - %ez = %cz + %bz; - %eye = %ex @ " " @ %ey @ " " @ %ez; - %euler = %pitch @ " 0 " @ (%yaw + $pi); - %rot = eulerRadToMatrix(%euler); - return %eye @ " " @ %rot; -} - -function RPGClient_EnsureCursor() -{ - if (!$RPGClient::Active || !isObject(Canvas)) - { - return; - } - if (Canvas.isCursorOn()) - { - return; - } - lockMouse(0); - cursorOn(); -} - -function RPGClient_CameraTick() -{ - $RPGClient::CamTickSched = 0; - if (!$RPGClient::Active) - { - return; - } - %spin = $RPGClient::YawSpeed * 2.5; - %zoom = $RPGClient::ZoomSpeed * 2.5; - %changed = 0; - if ($mvLeftAction > 0) - { - $RPGClient::Yaw = $RPGClient::Yaw - %spin * $mvLeftAction; - %changed = 1; - } - if ($mvRightAction > 0) - { - $RPGClient::Yaw = $RPGClient::Yaw + %spin * $mvRightAction; - %changed = 1; - } - if ($mvForwardAction > 0) - { - $RPGClient::Dist = getMax(6, $RPGClient::Dist - %zoom * $mvForwardAction); - %changed = 1; - } - if ($mvBackwardAction > 0) - { - $RPGClient::Dist = getMin(32, $RPGClient::Dist + %zoom * $mvBackwardAction); - %changed = 1; - } - if ($RPGClient::MvL > 0) - { - $RPGClient::Yaw = $RPGClient::Yaw - %spin * $RPGClient::MvL; - %changed = 1; - } - if ($RPGClient::MvR > 0) - { - $RPGClient::Yaw = $RPGClient::Yaw + %spin * $RPGClient::MvR; - %changed = 1; - } - if ($RPGClient::MvF > 0) - { - $RPGClient::Dist = getMax(6, $RPGClient::Dist - %zoom * $RPGClient::MvF); - %changed = 1; - } - if ($RPGClient::MvB > 0) - { - $RPGClient::Dist = getMin(32, $RPGClient::Dist + %zoom * $RPGClient::MvB); - %changed = 1; - } - RPGClient_ApplyCamera(%changed); - $RPGClient::CamTickSched = schedule(32, 0, RPGClient_CameraTick); -} - -function RPGClient_BumpKeyMap() -{ - if (!$RPGClient::Active || !isObject(RPGClient_KeyMap)) - { - return; - } - RPGClient_KeyMap.pop(); - RPGClient_KeyMap.push(); -} - -function RPGClient_StartCameraTick() -{ - cancel($RPGClient::CamTickSched); - $RPGClient::CamTickSched = schedule(32, 0, RPGClient_CameraTick); -} - -function RPGClient_InPlay() -{ - return isObject(ServerConnection) && ($RPGClient::Server || $RPGClient::Active); -} - -function RPGClient_BootPlay() -{ - if (!isObject(ServerConnection)) - { - return; - } - if ($RPGClient::PlayBooted) - { - RPGClient_EnsureOverlay(); - return; - } - $RPGClient::Server = 1; - $RPGClient::Active = 1; - $RPGClient::PlayBooted = 1; - if ($RPGClient::Dist < 6) - { - $RPGClient::Dist = 14; - } - RPGClient_EnsureOverlay(); - RPGClient_BindKeys(); - RPGClient_StartCameraTick(); - RPGClient_ScheduleCamera(); - RPGClient_EnsureCursor(); -} - -function RPGClient_KeyMvForward(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - $RPGClient::MvF = %val; -} - -function RPGClient_KeyMvBackward(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - $RPGClient::MvB = %val; -} - -function RPGClient_KeyMvLeft(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - $RPGClient::MvL = %val; -} - -function RPGClient_KeyMvRight(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - $RPGClient::MvR = %val; -} - -function RPGClient_EnsureGlobalKeys() -{ - if ($RPGClient::GlobalKeys || !isObject(GlobalActionMap)) - { - return; - } - GlobalActionMap.bind(keyboard, w, RPGClient_KeyMvForward); - GlobalActionMap.bind(keyboard, s, RPGClient_KeyMvBackward); - GlobalActionMap.bind(keyboard, a, RPGClient_KeyMvLeft); - GlobalActionMap.bind(keyboard, d, RPGClient_KeyMvRight); - GlobalActionMap.bind(keyboard, left, RPGClient_YawLeft); - GlobalActionMap.bind(keyboard, right, RPGClient_YawRight); - GlobalActionMap.bind(keyboard, equals, RPGClient_ZoomIn); - GlobalActionMap.bind(keyboard, minus, RPGClient_ZoomOut); - GlobalActionMap.bind(keyboard, "[", RPGClient_ZoomIn); - GlobalActionMap.bind(keyboard, "]", RPGClient_ZoomOut); - GlobalActionMap.bind(keyboard0, w, RPGClient_KeyMvForward); - GlobalActionMap.bind(keyboard0, s, RPGClient_KeyMvBackward); - GlobalActionMap.bind(keyboard0, a, RPGClient_KeyMvLeft); - GlobalActionMap.bind(keyboard0, d, RPGClient_KeyMvRight); - GlobalActionMap.bind(keyboard0, left, RPGClient_YawLeft); - GlobalActionMap.bind(keyboard0, right, RPGClient_YawRight); - GlobalActionMap.bind(keyboard0, equals, RPGClient_ZoomIn); - GlobalActionMap.bind(keyboard0, minus, RPGClient_ZoomOut); - $RPGClient::GlobalKeys = 1; -} - -function RPGClient_ClearGlobalKeys() -{ - if (!$RPGClient::GlobalKeys || !isObject(GlobalActionMap)) - { - return; - } - GlobalActionMap.unbind(keyboard, w); - GlobalActionMap.unbind(keyboard, s); - GlobalActionMap.unbind(keyboard, a); - GlobalActionMap.unbind(keyboard, d); - GlobalActionMap.unbind(keyboard, left); - GlobalActionMap.unbind(keyboard, right); - GlobalActionMap.unbind(keyboard, equals); - GlobalActionMap.unbind(keyboard, minus); - GlobalActionMap.unbind(keyboard, "["); - GlobalActionMap.unbind(keyboard, "]"); - GlobalActionMap.unbind(keyboard0, w); - GlobalActionMap.unbind(keyboard0, s); - GlobalActionMap.unbind(keyboard0, a); - GlobalActionMap.unbind(keyboard0, d); - GlobalActionMap.unbind(keyboard0, left); - GlobalActionMap.unbind(keyboard0, right); - GlobalActionMap.unbind(keyboard0, equals); - GlobalActionMap.unbind(keyboard0, minus); - $RPGClient::GlobalKeys = 0; -} - -function RPGClient_SendClickFromCursor(%button) -{ - if (!$RPGClient::Active || !isObject(Canvas)) - { - return; - } - RPGClient_SendClick(%button, Canvas.getCursorPos()); -} - -function RPGClient_EnsureKeyMap() -{ - if (!isObject(RPGClient_KeyMap)) - { - new ActionMap(RPGClient_KeyMap); - RPGClient_KeyMap.bind(keyboard, w, RPGClient_KeyMvForward); - RPGClient_KeyMap.bind(keyboard, s, RPGClient_KeyMvBackward); - RPGClient_KeyMap.bind(keyboard, a, RPGClient_KeyMvLeft); - RPGClient_KeyMap.bind(keyboard, d, RPGClient_KeyMvRight); - RPGClient_KeyMap.bind(keyboard0, w, RPGClient_KeyMvForward); - RPGClient_KeyMap.bind(keyboard0, s, RPGClient_KeyMvBackward); - RPGClient_KeyMap.bind(keyboard0, a, RPGClient_KeyMvLeft); - RPGClient_KeyMap.bind(keyboard0, d, RPGClient_KeyMvRight); - RPGClient_KeyMap.bind(keyboard, left, RPGClient_YawLeft); - RPGClient_KeyMap.bind(keyboard, right, RPGClient_YawRight); - RPGClient_KeyMap.bind(keyboard0, left, RPGClient_YawLeft); - RPGClient_KeyMap.bind(keyboard0, right, RPGClient_YawRight); - RPGClient_KeyMap.bind(keyboard, equals, RPGClient_ZoomIn); - RPGClient_KeyMap.bind(keyboard, minus, RPGClient_ZoomOut); - RPGClient_KeyMap.bind(keyboard, "[", RPGClient_ZoomIn); - RPGClient_KeyMap.bind(keyboard, "]", RPGClient_ZoomOut); - RPGClient_KeyMap.bind(keyboard0, equals, RPGClient_ZoomIn); - RPGClient_KeyMap.bind(keyboard0, minus, RPGClient_ZoomOut); - RPGClient_KeyMap.bind(keyboard, m, RPGClient_ToggleCursor); - RPGClient_KeyMap.bind(keyboard0, m, RPGClient_ToggleCursor); - } - if ($RPGClient::KeyMapPushed) - { - RPGClient_KeyMap.pop(); - } - RPGClient_KeyMap.push(); - $RPGClient::KeyMapPushed = 1; -} - -function RPGClient_HijackMoveMap() -{ - if ($RPGClient::MoveHijacked || !isObject(moveMap)) - { - return; - } - moveMap.bind(keyboard, w, RPGClient_KeyMvForward); - moveMap.bind(keyboard, s, RPGClient_KeyMvBackward); - moveMap.bind(keyboard, a, RPGClient_KeyMvLeft); - moveMap.bind(keyboard, d, RPGClient_KeyMvRight); - moveMap.bind(keyboard, left, RPGClient_YawLeft); - moveMap.bind(keyboard, right, RPGClient_YawRight); - moveMap.bind(keyboard, equals, RPGClient_ZoomIn); - moveMap.bind(keyboard, minus, RPGClient_ZoomOut); - moveMap.bind(keyboard, "[", RPGClient_ZoomIn); - moveMap.bind(keyboard, "]", RPGClient_ZoomOut); - $RPGClient::MoveHijacked = 1; -} - -function RPGClient_EnsureOverlay() -{ - if (!isObject(PlayGui)) - { - return; - } - if (!isObject(RPGClient_Mouse)) - { - new GuiMouseEventCtrl(RPGClient_Mouse) - { - className = "RPGClient_Mouse"; - horizSizing = "width"; - vertSizing = "height"; - position = "0 0"; - extent = "640 480"; - profile = "GuiDefaultProfile"; - visible = "1"; - enabled = "1"; - modal = "0"; - }; - PlayGui.add(RPGClient_Mouse); - PlayGui.pushToBack(RPGClient_Mouse); - } - RPGClient_EnsureKeyMap(); - RPGClient_HijackMoveMap(); - RPGClient_EnsureGlobalKeys(); - $enableDirectInput = 1; - activateDirectInput(); - $mvFreeLook = 0; - $firstPerson = 0; - if (isObject(Canvas) && !Canvas.isCursorOn()) - { - Canvas.cursorOn(); - } - if (isObject(PlayGui)) - { - PlayGui.killToolHud(); - } -} - -function RPGClient_EnableOverlay() -{ - RPGClient_EnsureOverlay(); -} - -function RPGClient_ToggleCursor(%val) -{ - if (!$RPGClient::Active || !%val) - { - return; - } - ToggleCursor(%val); -} - -function RPGClient_DisableOverlay() -{ - cancel($RPGClient::CamSched); - $RPGClient::CamSched = 0; - cancel($RPGClient::CamTickSched); - $RPGClient::CamTickSched = 0; - $RPGClient::MvF = 0; - $RPGClient::MvB = 0; - $RPGClient::MvL = 0; - $RPGClient::MvR = 0; - if ($RPGClient::KeyMapPushed && isObject(RPGClient_KeyMap)) - { - RPGClient_KeyMap.pop(); - $RPGClient::KeyMapPushed = 0; - } - if (isObject(RPGClient_Mouse)) - { - RPGClient_Mouse.delete(); - } - $RPGClient::Active = 0; - $RPGClient::OrbitInit = 0; - $RPGClient::HudPlaced = 0; - $RPGClient::MoveHijacked = 0; - $RPGClient::PlayBooted = 0; - RPGClient_ClearGlobalKeys(); -} - -function RPGClient_Hud_Place() -{ - if (!isObject(RPGClient_Hud) || !isObject(Canvas)) - { - return; - } - %ext = Canvas.getExtent(); - %w = getWord(%ext, 0); - %h = getWord(%ext, 1); - if (%h < 1) - { - return; - } - %pw = %w > 520 ? 520 : %w - 16; - %px = mFloor((%w - %pw) * 0.5); - %py = %h - 108; - if (%py < 8) - { - %py = 8; - } - RPGClient_Hud.resize(%px, %py, %pw, 20); - $RPGClient::HudPlaced = 1; -} - -function RPGClient_ApplyCamera(%forceSync) -{ - %conn = RPGClient_GetConn(); - if (!isObject(%conn)) - { - return; - } - %body = %conn.Player; - if (!isObject(%body)) - { - %body = %conn.player; - } - if (!isObject(%body)) - { - return; - } - %cam = %conn.getControlObject(); - if (!isObject(%cam) || %cam.getClassName() !$= "Camera") - { - %cam = %conn.Camera; - if (!isObject(%cam)) - { - %cam = %conn.camera; - } - } - if (!isObject(%cam)) - { - return; - } - %conn.setControlObject(%cam); - $mvFreeLook = 0; - $firstPerson = 0; - %xfm = RPGClient_ViewTransform(%body, $RPGClient::Yaw, $RPGClient::Dist, 0, 0); - %cam.unmountImage(0); - if (!$RPGClient::OrbitInit) - { - %cam.setMode("Corpse", %body); - %cam.setOrbitMode(%body, %xfm, 0, 32, $RPGClient::Dist); - %cam.mode = "Corpse"; - %cam.thirdPersonOnly = 1; - $RPGClient::OrbitInit = 1; - } - %cam.setTransform(%xfm); - %cam.setOrbitDist($RPGClient::Dist); - %now = getSimTime(); - if (%forceSync || %now - $RPGClient::LastCamSync > 120) - { - $RPGClient::LastCamSync = %now; - commandToServer('RPG_CamSync', $RPGClient::Yaw, $RPGClient::Dist, 0, 0); - } -} - -function RPGClient_ScheduleCamera() -{ - cancel($RPGClient::CamSched); - $RPGClient::CamSched = schedule(50, 0, RPGClient_ApplyCamera, 0); - schedule(200, 0, RPGClient_ApplyCamera, 0); - schedule(500, 0, RPGClient_ApplyCamera, 0); - schedule(1200, 0, RPGClient_ApplyCamera, 0); - schedule(2500, 0, RPGClient_ApplyCamera, 0); -} - -function RPGClient_YawLeft(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - if (%val) - { - $RPGClient::MvL = %val; - } - else - { - $RPGClient::MvL = 0; - } -} - -function RPGClient_YawRight(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - if (%val) - { - $RPGClient::MvR = %val; - } - else - { - $RPGClient::MvR = 0; - } -} - -function RPGClient_ZoomIn(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - if (%val) - { - $RPGClient::Dist = getMax(6, $RPGClient::Dist - 0.8); - RPGClient_ApplyCamera(1); - } -} - -function RPGClient_ZoomOut(%val) -{ - if (!RPGClient_InPlay()) - { - return; - } - if (%val) - { - $RPGClient::Dist = getMin(32, $RPGClient::Dist + 0.8); - RPGClient_ApplyCamera(1); - } -} - -function RPGClient_CanSendCmd() -{ - return isObject(ServerConnection) || isObject(localClientConnection); -} - -function RPGClient_GetConn() -{ - if (isObject(ServerConnection)) - { - return ServerConnection; - } - if (isObject(localClientConnection)) - { - return localClientConnection; - } - return 0; -} - -function RPGClient_SendHello() -{ - if ($RPGClient::Server) - { - return; - } - if (!RPGClient_CanSendCmd()) - { - return; - } - %now = getSimTime(); - if ($RPGClient::HelloLast !$= "" && %now - $RPGClient::HelloLast < 900) - { - return; - } - $RPGClient::HelloLast = %now; - commandToServer('RPG_Hello', $RPGClient::Version); - commandToServer('RPGHello', $RPGClient::Version); - commandToServer('Message', "/rpg clienthello " @ $RPGClient::Version); -} - -function clientCmdRPG_Probe() -{ - RPGClient_SendHello(); -} - -function clientCmdRPG_Welcome(%serverProto, %accepted) -{ - if (%accepted && %serverProto == $RPGClient::Version) - { - $RPGClient::HelloPending = 0; - $RPGClient::Server = 1; - schedule(0, 0, RPGClient_BootPlay); - schedule(2500, 0, RPGClient_TopDownWatch); - } - else if (!%accepted) - { - echo("Client_RPG: server rejected handshake (server proto " @ %serverProto @ ")"); - schedule(1500, 0, RPGClient_SendHello); - } -} - -function RPGClient_TopDownWatch() -{ - $RPGClient::TopDownWatchSched = 0; - if ($RPGClient::Active || $RPGClient::Server || !RPGClient_CanSendCmd()) - { - return; - } - $RPGClient::HelloLast = ""; - RPGClient_SendHello(); - $RPGClient::TopDownWatchSched = schedule(2500, 0, RPGClient_TopDownWatch); -} - -function RPGClient_EnsurePlayGui() -{ - if (!isObject(PlayGui) || !isObject(Canvas)) - { - return 0; - } - if (Canvas.getContent() != PlayGui.getId()) - { - Canvas.setContent(PlayGui); - } - return 1; -} - -function clientCmdRPG_TopDown(%on, %yaw, %dist) -{ - if (%on) - { - $RPGClient::Server = 1; - $RPGClient::Active = 1; - $RPGClient::PlayBooted = 1; - if (%dist > 0) - { - $RPGClient::Dist = %dist; - } - $RPGClient::Yaw = %yaw; - if (!RPGClient_EnsurePlayGui()) - { - RPGClient_BootPlay(); - schedule(200, 0, clientCmdRPG_TopDown, %on, %yaw, %dist); - return; - } - cancel($RPGClient::TopDownWatchSched); - $RPGClient::TopDownWatchSched = 0; - if (%dist > 0) - { - $RPGClient::Dist = %dist; - } - else if ($RPGClient::Dist < 6) - { - $RPGClient::Dist = 14; - } - $RPGClient::Yaw = %yaw; - $RPGClient::OrbitInit = 0; - $RPGClient::HudPlaced = 0; - RPGClient_EnsureOverlay(); - RPGClient_BindKeys(); - RPGClient_StartCameraTick(); - schedule(500, 0, RPGClient_EnsureOverlay); - schedule(1500, 0, RPGClient_EnsureOverlay); - RPGClient_ScheduleCamera(); - RPGClient_EnsureCursor(); - RPGClient_Hud_Ensure(); - } - else - { - $RPGClient::Server = 0; - $RPGClient::PlayBooted = 0; - RPGClient_DisableOverlay(); - RPGClient_UnbindKeys(); - } -} - -function clientCmdRPG_Hud(%hp, %maxHp, %silver, %load, %cap, %skillId, %skillValue, %blocking, %tHp, %tMax) -{ - if (!RPGClient_InPlay()) - { - RPGClient_BootPlay(); - } - RPGClient_Hud_Update(%hp, %maxHp, %silver, %load, %cap, %skillId, %skillValue, %tHp, %tMax); -} - -function clientCmdRPG_HudOff() -{ - RPGClient_Hud_Hide(); -} - -function clientCmdRPG_Notify(%text, %sec) -{ - if (%text !$= "") - { - RPGClient_Notify_Show(%text, %sec); - } -} - -function clientCmdRPG_Dialog(%title, %body, %sec) -{ - RPGClient_Dialog_Show(%title, %body, %sec); -} - -function clientCmdRPG_ListBegin(%title, %page, %pages) -{ - RPGClient_List_Begin(%title, %page, %pages); -} - -function clientCmdRPG_ListRow(%row) -{ - RPGClient_List_Row(%row); -} - -function clientCmdRPG_ListEnd() -{ - RPGClient_List_End(); -} - -// --- HUD / list / dialog (Phase 4): thin windows on PlayGui --- -function RPGClient_Hud_Ensure() -{ - if (isObject(RPGClient_Hud)) - { - return; - } - new GuiTextCtrl(RPGClient_Hud) - { - horizSizing = "absolute"; - vertSizing = "absolute"; - position = "8 400"; - extent = "520 20"; - profile = "ChatHudMessageProfile"; - text = ""; - }; - PlayGui.add(RPGClient_Hud); - RPGClient_Hud_Place(); -} - -function RPGClient_Hud_Update(%hp, %maxHp, %silver, %load, %cap, %skillId, %skillValue, %tHp, %tMax) -{ - if (!$RPGClient::Active) - { - return; - } - RPGClient_Hud_Ensure(); - if (!$RPGClient::HudPlaced) - { - RPGClient_Hud_Place(); - } - %t = ""; - if (%tMax > 0) - { - %t = " | Target " @ %tHp @ "/" @ %tMax; - } - RPGClient_Hud.setText("HP " @ %hp @ "/" @ %maxHp @ %t @ " | " @ %silver @ " silver | load " @ %load @ "/" @ %cap @ " | " @ %skillId @ " " @ %skillValue); -} - -function RPGClient_Hud_Hide() -{ - if (isObject(RPGClient_Hud)) - { - RPGClient_Hud.setText(""); - } -} - -function RPGClient_Notify_Show(%text, %sec) -{ - RPGClient_Hud_Ensure(); - RPGClient_Hud.setText(%text); - if (%sec > 0) - { - schedule(%sec * 1000, RPGClient_Hud, setText, ""); - } -} - -function RPGClient_Dialog_Show(%title, %body, %sec) -{ - messageBoxOK(%title, %body, ""); -} - -function RPGClient_List_Begin(%title, %page, %pages) -{ - $RPGClient::ListBody = ""; - $RPGClient::ListTitle = %title @ " (" @ (%page + 1) @ "/" @ %pages @ ")"; -} - -function RPGClient_List_Row(%row) -{ - $RPGClient::ListBody = $RPGClient::ListBody @ %row @ "\n"; -} - -function RPGClient_List_End() -{ - if ($RPGClient::ListBody !$= "") - { - messageBoxOK($RPGClient::ListTitle, $RPGClient::ListBody, ""); - } -} - -package RPGClient_BlockMouseFire -{ - function mouseFire(%val) - { - if ($RPGClient::Active) - { - if (%val) - { - RPGClient_SendClickFromCursor(0); - } - return; - } - Parent::mouseFire(%val); - } - - function altTrigger(%val) - { - if ($RPGClient::Active) - { - if (%val) - { - RPGClient_SendClickFromCursor(1); - } - return; - } - Parent::altTrigger(%val); - } -}; -activatePackage(RPGClient_BlockMouseFire); - -package RPGClient_PageKeys -{ - function PageUpNewChatHud(%val) - { - if ($RPGClient::Active && %val) - { - RPGClient_ZoomIn(1); - return; - } - Parent::PageUpNewChatHud(%val); - } - - function PageDownNewChatHud(%val) - { - if ($RPGClient::Active && %val) - { - RPGClient_ZoomOut(1); - return; - } - Parent::PageDownNewChatHud(%val); - } -}; -activatePackage(RPGClient_PageKeys); - -package RPGClient_Input -{ - function moveforward(%val) - { - if (RPGClient_InPlay()) - { - RPGClient_KeyMvForward(%val); - return; - } - Parent::moveforward(%val); - } - - function movebackward(%val) - { - if (RPGClient_InPlay()) - { - RPGClient_KeyMvBackward(%val); - return; - } - Parent::movebackward(%val); - } - - function moveleft(%val) - { - if (RPGClient_InPlay()) - { - RPGClient_KeyMvLeft(%val); - return; - } - Parent::moveleft(%val); - } - - function moveright(%val) - { - if (RPGClient_InPlay()) - { - RPGClient_KeyMvRight(%val); - return; - } - Parent::moveright(%val); - } - - function invLeft(%val) - { - if (RPGClient_InPlay()) - { - RPGClient_YawLeft(%val); - return; - } - Parent::invLeft(%val); - } - - function invRight(%val) - { - if (RPGClient_InPlay()) - { - RPGClient_YawRight(%val); - return; - } - Parent::invRight(%val); - } -}; -activatePackage(RPGClient_Input); - -package RPGClient_MapStack -{ - function noHudGui::onWake(%this) - { - Parent::onWake(%this); - if ($RPGClient::Active) - { - schedule(0, 0, RPGClient_EnsureOverlay); - } - } -}; -activatePackage(RPGClient_MapStack); - -package RPGClient_Cursor -{ - function GuiCanvas::checkCursor(%this) - { - Parent::checkCursor(%this); - if ($RPGClient::Active && !%this.isCursorOn()) - { - lockMouse(0); - %this.cursorOn(); - } - } - - function escapeMenu::toggle(%this) - { - if (%this.isAwake()) - { - Canvas.popDialog(%this); - if ($RPGClient::Active) - { - schedule(0, 0, RPGClient_EnsureCursor); - schedule(0, 0, RPGClient_EnsureOverlay); - } - return; - } - if (Canvas.getContent().getName() $= "LoadingGui") - { - disconnect(); - return; - } - Canvas.pushDialog(%this); - } -}; -activatePackage(RPGClient_Cursor); - -package RPGClient_ViewLock -{ - function toggleFirstPerson(%val) - { - if ($RPGClient::Active) - { - return; - } - Parent::toggleFirstPerson(%val); - } - - function toggleFreeLook(%val) - { - if ($RPGClient::Active) - { - return; - } - Parent::toggleFreeLook(%val); - } - - function dropCameraAtPlayer(%val) - { - if ($RPGClient::Active) - { - return; - } - Parent::dropCameraAtPlayer(%val); - } -}; -activatePackage(RPGClient_ViewLock); - -package RPGClient_Cleanup -{ - function disconnectedCleanup(%doReconnect) - { - RPGClient_DisableOverlay(); - RPGClient_UnbindKeys(); - $RPGClient::Server = 0; - $RPGClient::HelloPending = 0; - $RPGClient::HelloLast = ""; - cancel($RPGClient::TopDownWatchSched); - $RPGClient::TopDownWatchSched = 0; - $RPGClient::Server = 0; - $RPGClient::PlayBooted = 0; - RPGClient_ClearGlobalKeys(); - Parent::disconnectedCleanup(%doReconnect); - } -}; -activatePackage(RPGClient_Cleanup); - -package RPGClient_Mission -{ - function onMissionDownloadComplete() - { - Parent::onMissionDownloadComplete(); - schedule(250, 0, RPGClient_SendHello); - } -}; -activatePackage(RPGClient_Mission); - -echo("Client_RPG loaded (protocol " @ $RPGClient::Version @ ")"); +// Client_RPG - top-down camera, click-to-move, and RPG windows for GameMode_RPG. +exec("./ui.cs"); +// The server probes with RPG_Probe; reply with serverCmdRPG_Hello(2) to play. + +$RPGClient::Version = 2; +$RPGClient::Active = 0; +$RPGClient::Yaw = 0; +$RPGClient::Dist = 14; +$RPGClient::TopDownPitch = 1.22; +$RPGClient::OrbitInit = 0; +$RPGClient::HudPlaced = 0; +$RPGClient::KeyMapPushed = 0; +$RPGClient::MoveHijacked = 0; +$RPGClient::GlobalKeys = 0; +$RPGClient::PlayBooted = 0; +$RPGClient::CamTickOn = 0; +$RPGClient::PanX = 0; +$RPGClient::PanY = 0; +$RPGClient::PanMax = 48; +$RPGClient::YawSpeed = 0.045; +$RPGClient::ZoomSpeed = 0.12; +$RPGClient::CamTickSched = 0; +$RPGClient::LastCamSync = 0; +$RPGClient::MvF = 0; +$RPGClient::MvB = 0; +$RPGClient::MvL = 0; +$RPGClient::MvR = 0; +$RPGClient::CamModeReady = 0; +$RPGClient::HelloPending = 0; +$RPGClient::HelloLast = ""; +$RPGClient::TopDownWatchSched = 0; +$RPGClient::CamSched = 0; +$RPGClient::LastClickTime = 0; + +function RPGClient_NormPos(%ctrl, %pos) +{ + if (!isObject(%ctrl)) + { + return "0.5 0.5"; + } + %ext = %ctrl.getExtent(); + %w = getWord(%ext, 0); + %h = getWord(%ext, 1); + if (%w < 1) + { + %w = 1; + } + if (%h < 1) + { + %h = 1; + } + %x = getWord(%pos, 0) / %w; + %y = 1 - getWord(%pos, 1) / %h; + if (%x < 0) + { + %x = 0; + } + if (%x > 1) + { + %x = 1; + } + if (%y < 0) + { + %y = 0; + } + if (%y > 1) + { + %y = 1; + } + return %x SPC %y; +} + +function RPGClient_Aspect() +{ + if (!isObject(Canvas)) + { + return 1.333; + } + %ext = Canvas.getExtent(); + %w = getWord(%ext, 0); + %h = getWord(%ext, 1); + return %h > 0 ? %w / %h : 1.333; +} + +function RPGClient_SendClick(%button, %pos) +{ + if (!isObject(ServerConnection)) + { + return; + } + %now = getSimTime(); + if (%now - $RPGClient::LastClickTime < 80) + { + return; + } + $RPGClient::LastClickTime = %now; + %norm = RPGClient_NormPos(RPGClient_Mouse, %pos); + commandToServer('RPG_Click', %button, getWord(%norm, 0), getWord(%norm, 1), RPGClient_Aspect()); + schedule(1, 0, RPGClient_ApplyCamera, 1); +} + +function RPGClient_Mouse::onMouseDown(%this, %mod, %pos, %clicks) +{ + if (!$RPGClient::Active) + { + return; + } + RPGClient_SendClick(0, %pos); +} + +function RPGClient_Mouse::onRightMouseDown(%this, %mod, %pos, %clicks) +{ + if (!$RPGClient::Active) + { + return; + } + RPGClient_SendClick(1, %pos); +} + +function RPGClient_Mouse::onWake(%this) +{ + if (isObject(Canvas)) + { + %ext = Canvas.getExtent(); + %this.resize(0, 0, getWord(%ext, 0), getWord(%ext, 1)); + } +} + +function RPGClient_ClampPan() +{ + %max = $RPGClient::PanMax; + if ($RPGClient::PanX > %max) + { + $RPGClient::PanX = %max; + } + if ($RPGClient::PanX < -%max) + { + $RPGClient::PanX = -%max; + } + if ($RPGClient::PanY > %max) + { + $RPGClient::PanY = %max; + } + if ($RPGClient::PanY < -%max) + { + $RPGClient::PanY = -%max; + } +} + +function RPGClient_ViewTransform(%body, %yaw, %dist, %panX, %panY) +{ + %p = %body.getPosition(); + %cx = getWord(%p, 0) + %panX; + %cy = getWord(%p, 1) + %panY; + %cz = getWord(%p, 2) + 1.5; + %pitch = $RPGClient::TopDownPitch; + %bx = -mCos(%yaw) * mCos(%pitch) * %dist; + %by = -mSin(%yaw) * mCos(%pitch) * %dist; + %bz = mSin(%pitch) * %dist; + %ex = %cx + %bx; + %ey = %cy + %by; + %ez = %cz + %bz; + %eye = %ex @ " " @ %ey @ " " @ %ez; + %euler = %pitch @ " 0 " @ (%yaw + $pi); + %rot = eulerRadToMatrix(%euler); + return %eye @ " " @ %rot; +} + +function RPGClient_EnsureCursor() +{ + if (!$RPGClient::Active || !isObject(Canvas)) + { + return; + } + if (Canvas.isCursorOn()) + { + return; + } + lockMouse(0); + cursorOn(); +} + +function RPGClient_CameraTick() +{ + $RPGClient::CamTickSched = 0; + if (!isObject(ServerConnection) || !$RPGClient::CamTickOn) + { + return; + } + %spin = $RPGClient::YawSpeed * 2.5; + %zoom = $RPGClient::ZoomSpeed * 2.5; + %changed = 0; + if ($mvLeftAction > 0) + { + $RPGClient::Yaw = $RPGClient::Yaw - %spin * $mvLeftAction; + %changed = 1; + } + if ($mvRightAction > 0) + { + $RPGClient::Yaw = $RPGClient::Yaw + %spin * $mvRightAction; + %changed = 1; + } + if ($mvForwardAction > 0) + { + $RPGClient::Dist = getMax(6, $RPGClient::Dist - %zoom * $mvForwardAction); + %changed = 1; + } + if ($mvBackwardAction > 0) + { + $RPGClient::Dist = getMin(32, $RPGClient::Dist + %zoom * $mvBackwardAction); + %changed = 1; + } + if ($RPGClient::MvL > 0) + { + $RPGClient::Yaw = $RPGClient::Yaw - %spin * $RPGClient::MvL; + %changed = 1; + } + if ($RPGClient::MvR > 0) + { + $RPGClient::Yaw = $RPGClient::Yaw + %spin * $RPGClient::MvR; + %changed = 1; + } + if ($RPGClient::MvF > 0) + { + $RPGClient::Dist = getMax(6, $RPGClient::Dist - %zoom * $RPGClient::MvF); + %changed = 1; + } + if ($RPGClient::MvB > 0) + { + $RPGClient::Dist = getMin(32, $RPGClient::Dist + %zoom * $RPGClient::MvB); + %changed = 1; + } + RPGClient_ApplyCamera(%changed); + $RPGClient::CamTickSched = schedule(32, 0, RPGClient_CameraTick); +} + +function RPGClient_BumpKeyMap() +{ + if (!$RPGClient::Active || !isObject(RPGClient_KeyMap)) + { + return; + } + RPGClient_KeyMap.pop(); + RPGClient_KeyMap.push(); +} + +function RPGClient_StartCameraTick() +{ + cancel($RPGClient::CamTickSched); + $RPGClient::CamTickSched = schedule(32, 0, RPGClient_CameraTick); +} + +function RPGClient_InPlay() +{ + return isObject(ServerConnection) && ($RPGClient::Server || $RPGClient::Active); +} + +function RPGClient_BootPlay() +{ + if (!isObject(ServerConnection)) + { + return; + } + $RPGClient::Server = 1; + $RPGClient::Active = 1; + $RPGClient::CamTickOn = 1; + $RPGClient::PlayBooted = 1; + if ($RPGClient::Dist < 6) + { + $RPGClient::Dist = 14; + } + RPGClient_EnsureOverlay(); + RPGClient_BindKeys(); + RPGClient_StartCameraTick(); + RPGClient_ScheduleCamera(); + RPGClient_EnsureCursor(); +} + +function RPGClient_KeyMvForward(%val) +{ + if (!isObject(ServerConnection)) + { + return; + } + if (!$RPGClient::CamTickOn) + { + moveforward(%val); + return; + } + $RPGClient::MvF = %val; +} + +function RPGClient_KeyMvBackward(%val) +{ + if (!isObject(ServerConnection)) + { + return; + } + if (!$RPGClient::CamTickOn) + { + movebackward(%val); + return; + } + $RPGClient::MvB = %val; +} + +function RPGClient_KeyMvLeft(%val) +{ + if (!isObject(ServerConnection)) + { + return; + } + if (!$RPGClient::CamTickOn) + { + moveleft(%val); + return; + } + $RPGClient::MvL = %val; +} + +function RPGClient_KeyMvRight(%val) +{ + if (!isObject(ServerConnection)) + { + return; + } + if (!$RPGClient::CamTickOn) + { + moveright(%val); + return; + } + $RPGClient::MvR = %val; +} + +function RPGClient_RestoreMoveMap() +{ + if (!isObject(moveMap)) + { + return; + } + moveMap.bind(keyboard, w, moveforward); + moveMap.bind(keyboard, s, movebackward); + moveMap.bind(keyboard, a, moveleft); + moveMap.bind(keyboard, d, moveright); + moveMap.bind(keyboard, left, invLeft); + moveMap.bind(keyboard, right, invRight); +} + +function RPGClient_EnsureGlobalKeys() +{ + if ($RPGClient::GlobalKeys || !isObject(GlobalActionMap)) + { + return; + } + GlobalActionMap.bind(keyboard, w, RPGClient_KeyMvForward); + GlobalActionMap.bind(keyboard, s, RPGClient_KeyMvBackward); + GlobalActionMap.bind(keyboard, a, RPGClient_KeyMvLeft); + GlobalActionMap.bind(keyboard, d, RPGClient_KeyMvRight); + GlobalActionMap.bind(keyboard, left, RPGClient_YawLeft); + GlobalActionMap.bind(keyboard, right, RPGClient_YawRight); + GlobalActionMap.bind(keyboard, equals, RPGClient_ZoomIn); + GlobalActionMap.bind(keyboard, minus, RPGClient_ZoomOut); + GlobalActionMap.bind(keyboard, "[", RPGClient_ZoomIn); + GlobalActionMap.bind(keyboard, "]", RPGClient_ZoomOut); + GlobalActionMap.bind(keyboard0, w, RPGClient_KeyMvForward); + GlobalActionMap.bind(keyboard0, s, RPGClient_KeyMvBackward); + GlobalActionMap.bind(keyboard0, a, RPGClient_KeyMvLeft); + GlobalActionMap.bind(keyboard0, d, RPGClient_KeyMvRight); + GlobalActionMap.bind(keyboard0, left, RPGClient_YawLeft); + GlobalActionMap.bind(keyboard0, right, RPGClient_YawRight); + GlobalActionMap.bind(keyboard0, equals, RPGClient_ZoomIn); + GlobalActionMap.bind(keyboard0, minus, RPGClient_ZoomOut); + $RPGClient::GlobalKeys = 1; +} + +function RPGClient_ClearGlobalKeys() +{ + if (!$RPGClient::GlobalKeys || !isObject(GlobalActionMap)) + { + return; + } + GlobalActionMap.unbind(keyboard, w); + GlobalActionMap.unbind(keyboard, s); + GlobalActionMap.unbind(keyboard, a); + GlobalActionMap.unbind(keyboard, d); + GlobalActionMap.unbind(keyboard, left); + GlobalActionMap.unbind(keyboard, right); + GlobalActionMap.unbind(keyboard, equals); + GlobalActionMap.unbind(keyboard, minus); + GlobalActionMap.unbind(keyboard, "["); + GlobalActionMap.unbind(keyboard, "]"); + GlobalActionMap.unbind(keyboard0, w); + GlobalActionMap.unbind(keyboard0, s); + GlobalActionMap.unbind(keyboard0, a); + GlobalActionMap.unbind(keyboard0, d); + GlobalActionMap.unbind(keyboard0, left); + GlobalActionMap.unbind(keyboard0, right); + GlobalActionMap.unbind(keyboard0, equals); + GlobalActionMap.unbind(keyboard0, minus); + $RPGClient::GlobalKeys = 0; +} + +function RPGClient_SendClickFromCursor(%button) +{ + if (!$RPGClient::Active || !isObject(Canvas)) + { + return; + } + RPGClient_SendClick(%button, Canvas.getCursorPos()); +} + +function RPGClient_EnsureKeyMap() +{ + if (!isObject(RPGClient_KeyMap)) + { + new ActionMap(RPGClient_KeyMap); + RPGClient_KeyMap.bind(keyboard, w, RPGClient_KeyMvForward); + RPGClient_KeyMap.bind(keyboard, s, RPGClient_KeyMvBackward); + RPGClient_KeyMap.bind(keyboard, a, RPGClient_KeyMvLeft); + RPGClient_KeyMap.bind(keyboard, d, RPGClient_KeyMvRight); + RPGClient_KeyMap.bind(keyboard0, w, RPGClient_KeyMvForward); + RPGClient_KeyMap.bind(keyboard0, s, RPGClient_KeyMvBackward); + RPGClient_KeyMap.bind(keyboard0, a, RPGClient_KeyMvLeft); + RPGClient_KeyMap.bind(keyboard0, d, RPGClient_KeyMvRight); + RPGClient_KeyMap.bind(keyboard, left, RPGClient_YawLeft); + RPGClient_KeyMap.bind(keyboard, right, RPGClient_YawRight); + RPGClient_KeyMap.bind(keyboard0, left, RPGClient_YawLeft); + RPGClient_KeyMap.bind(keyboard0, right, RPGClient_YawRight); + RPGClient_KeyMap.bind(keyboard, equals, RPGClient_ZoomIn); + RPGClient_KeyMap.bind(keyboard, minus, RPGClient_ZoomOut); + RPGClient_KeyMap.bind(keyboard, "[", RPGClient_ZoomIn); + RPGClient_KeyMap.bind(keyboard, "]", RPGClient_ZoomOut); + RPGClient_KeyMap.bind(keyboard0, equals, RPGClient_ZoomIn); + RPGClient_KeyMap.bind(keyboard0, minus, RPGClient_ZoomOut); + RPGClient_KeyMap.bind(keyboard, m, RPGClient_ToggleCursor); + RPGClient_KeyMap.bind(keyboard0, m, RPGClient_ToggleCursor); + } + if ($RPGClient::KeyMapPushed) + { + RPGClient_KeyMap.pop(); + } + RPGClient_KeyMap.push(); + $RPGClient::KeyMapPushed = 1; +} + +function RPGClient_HijackMoveMap() +{ + if ($RPGClient::MoveHijacked || !isObject(moveMap)) + { + return; + } + moveMap.bind(keyboard, w, RPGClient_KeyMvForward); + moveMap.bind(keyboard, s, RPGClient_KeyMvBackward); + moveMap.bind(keyboard, a, RPGClient_KeyMvLeft); + moveMap.bind(keyboard, d, RPGClient_KeyMvRight); + moveMap.bind(keyboard, left, RPGClient_YawLeft); + moveMap.bind(keyboard, right, RPGClient_YawRight); + moveMap.bind(keyboard, equals, RPGClient_ZoomIn); + moveMap.bind(keyboard, minus, RPGClient_ZoomOut); + moveMap.bind(keyboard, "[", RPGClient_ZoomIn); + moveMap.bind(keyboard, "]", RPGClient_ZoomOut); + $RPGClient::MoveHijacked = 1; +} + +function RPGClient_EnsureOverlay() +{ + if (!isObject(PlayGui)) + { + return; + } + if (!isObject(RPGClient_Mouse)) + { + new GuiMouseEventCtrl(RPGClient_Mouse) + { + className = "RPGClient_Mouse"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + profile = "GuiDefaultProfile"; + visible = "1"; + enabled = "1"; + modal = "0"; + }; + PlayGui.add(RPGClient_Mouse); + PlayGui.pushToBack(RPGClient_Mouse); + } + RPGClient_EnsureKeyMap(); + RPGClient_EnsureGlobalKeys(); + $enableDirectInput = 1; + activateDirectInput(); + $mvFreeLook = 0; + $firstPerson = 0; + if (isObject(Canvas) && !Canvas.isCursorOn()) + { + Canvas.cursorOn(); + } + if (isObject(PlayGui)) + { + PlayGui.killToolHud(); + } +} + +function RPGClient_EnableOverlay() +{ + RPGClient_EnsureOverlay(); +} + +function RPGClient_ToggleCursor(%val) +{ + if (!$RPGClient::Active || !%val) + { + return; + } + ToggleCursor(%val); +} + +function RPGClient_DisableOverlay() +{ + cancel($RPGClient::CamSched); + $RPGClient::CamSched = 0; + cancel($RPGClient::CamTickSched); + $RPGClient::CamTickSched = 0; + $RPGClient::MvF = 0; + $RPGClient::MvB = 0; + $RPGClient::MvL = 0; + $RPGClient::MvR = 0; + if ($RPGClient::KeyMapPushed && isObject(RPGClient_KeyMap)) + { + RPGClient_KeyMap.pop(); + $RPGClient::KeyMapPushed = 0; + } + if (isObject(RPGClient_Mouse)) + { + RPGClient_Mouse.delete(); + } + $RPGClient::Active = 0; + $RPGClient::OrbitInit = 0; + $RPGClient::HudPlaced = 0; + $RPGClient::MoveHijacked = 0; + $RPGClient::PlayBooted = 0; + $RPGClient::CamTickOn = 0; + RPGClient_ClearGlobalKeys(); + RPGClient_RestoreMoveMap(); +} + +function RPGClient_Hud_Place() +{ + if (!isObject(RPGClient_Hud) || !isObject(Canvas)) + { + return; + } + %ext = Canvas.getExtent(); + %w = getWord(%ext, 0); + %h = getWord(%ext, 1); + if (%h < 1) + { + return; + } + %pw = %w > 520 ? 520 : %w - 16; + %px = mFloor((%w - %pw) * 0.5); + %py = %h - 108; + if (%py < 8) + { + %py = 8; + } + RPGClient_Hud.resize(%px, %py, %pw, 20); + $RPGClient::HudPlaced = 1; +} + +function RPGClient_ApplyCamera(%forceSync) +{ + %conn = RPGClient_GetConn(); + if (!isObject(%conn)) + { + return; + } + %body = %conn.Player; + if (!isObject(%body)) + { + %body = %conn.player; + } + if (!isObject(%body)) + { + return; + } + %cam = %conn.getControlObject(); + if (!isObject(%cam) || %cam.getClassName() !$= "Camera") + { + %cam = %conn.Camera; + if (!isObject(%cam)) + { + %cam = %conn.camera; + } + } + if (!isObject(%cam)) + { + return; + } + %conn.setControlObject(%cam); + $mvFreeLook = 0; + $firstPerson = 0; + %xfm = RPGClient_ViewTransform(%body, $RPGClient::Yaw, $RPGClient::Dist, 0, 0); + %cam.unmountImage(0); + if (!$RPGClient::OrbitInit) + { + %cam.setMode("Corpse", %body); + %cam.setOrbitMode(%body, %xfm, 0, 32, $RPGClient::Dist); + %cam.mode = "Corpse"; + %cam.thirdPersonOnly = 1; + $RPGClient::OrbitInit = 1; + } + %cam.setTransform(%xfm); + %cam.setOrbitDist($RPGClient::Dist); + %now = getSimTime(); + if (%forceSync || %now - $RPGClient::LastCamSync > 120) + { + $RPGClient::LastCamSync = %now; + commandToServer('RPG_CamSync', $RPGClient::Yaw, $RPGClient::Dist, 0, 0); + } +} + +function RPGClient_ScheduleCamera() +{ + cancel($RPGClient::CamSched); + $RPGClient::CamSched = schedule(50, 0, RPGClient_ApplyCamera, 0); + schedule(200, 0, RPGClient_ApplyCamera, 0); + schedule(500, 0, RPGClient_ApplyCamera, 0); + schedule(1200, 0, RPGClient_ApplyCamera, 0); + schedule(2500, 0, RPGClient_ApplyCamera, 0); +} + +function RPGClient_YawLeft(%val) +{ + if (!$RPGClient::CamTickOn) + { + invLeft(%val); + return; + } + if (%val) + { + $RPGClient::MvL = %val; + } + else + { + $RPGClient::MvL = 0; + } +} + +function RPGClient_YawRight(%val) +{ + if (!$RPGClient::CamTickOn) + { + invRight(%val); + return; + } + if (%val) + { + $RPGClient::MvR = %val; + } + else + { + $RPGClient::MvR = 0; + } +} + +function RPGClient_ZoomIn(%val) +{ + if (!$RPGClient::CamTickOn) + { + return; + } + if (%val) + { + $RPGClient::Dist = getMax(6, $RPGClient::Dist - 0.8); + RPGClient_ApplyCamera(1); + } +} + +function RPGClient_ZoomOut(%val) +{ + if (!$RPGClient::CamTickOn) + { + return; + } + if (%val) + { + $RPGClient::Dist = getMin(32, $RPGClient::Dist + 0.8); + RPGClient_ApplyCamera(1); + } +} + +function RPGClient_CanSendCmd() +{ + return isObject(ServerConnection) || isObject(localClientConnection); +} + +function RPGClient_GetConn() +{ + if (isObject(ServerConnection)) + { + return ServerConnection; + } + if (isObject(localClientConnection)) + { + return localClientConnection; + } + return 0; +} + +function RPGClient_SendHello() +{ + if ($RPGClient::Server) + { + return; + } + if (!RPGClient_CanSendCmd()) + { + return; + } + %now = getSimTime(); + if ($RPGClient::HelloLast !$= "" && %now - $RPGClient::HelloLast < 900) + { + return; + } + $RPGClient::HelloLast = %now; + commandToServer('RPG_Hello', $RPGClient::Version); + commandToServer('RPGHello', $RPGClient::Version); + commandToServer('Message', "/rpg clienthello " @ $RPGClient::Version); +} + +function clientCmdRPG_Probe() +{ + RPGClient_SendHello(); +} + +function clientCmdRPG_Welcome(%serverProto, %accepted) +{ + if (%accepted && %serverProto == $RPGClient::Version) + { + $RPGClient::HelloPending = 0; + $RPGClient::Server = 1; + schedule(0, 0, RPGClient_BootPlay); + schedule(2500, 0, RPGClient_TopDownWatch); + } + else if (!%accepted) + { + echo("Client_RPG: server rejected handshake (server proto " @ %serverProto @ ")"); + schedule(1500, 0, RPGClient_SendHello); + } +} + +function RPGClient_TopDownWatch() +{ + $RPGClient::TopDownWatchSched = 0; + if ($RPGClient::Active || $RPGClient::Server || !RPGClient_CanSendCmd()) + { + return; + } + $RPGClient::HelloLast = ""; + RPGClient_SendHello(); + $RPGClient::TopDownWatchSched = schedule(2500, 0, RPGClient_TopDownWatch); +} + +function RPGClient_EnsurePlayGui() +{ + if (!isObject(PlayGui) || !isObject(Canvas)) + { + return 0; + } + if (Canvas.getContent() != PlayGui.getId()) + { + Canvas.setContent(PlayGui); + } + return 1; +} + +function clientCmdRPG_TopDown(%on, %yaw, %dist) +{ + if (%on) + { + $RPGClient::Server = 1; + $RPGClient::Active = 1; + $RPGClient::PlayBooted = 1; + if (%dist > 0) + { + $RPGClient::Dist = %dist; + } + $RPGClient::Yaw = %yaw; + if (!RPGClient_EnsurePlayGui()) + { + RPGClient_BootPlay(); + schedule(200, 0, clientCmdRPG_TopDown, %on, %yaw, %dist); + return; + } + cancel($RPGClient::TopDownWatchSched); + $RPGClient::TopDownWatchSched = 0; + if (%dist > 0) + { + $RPGClient::Dist = %dist; + } + else if ($RPGClient::Dist < 6) + { + $RPGClient::Dist = 14; + } + $RPGClient::Yaw = %yaw; + $RPGClient::OrbitInit = 0; + $RPGClient::HudPlaced = 0; + $RPGClient::CamTickOn = 1; + RPGClient_EnsureOverlay(); + RPGClient_BindKeys(); + RPGClient_StartCameraTick(); + schedule(500, 0, RPGClient_EnsureOverlay); + schedule(1500, 0, RPGClient_EnsureOverlay); + RPGClient_ScheduleCamera(); + RPGClient_EnsureCursor(); + RPGClient_Hud_Ensure(); + } + else + { + $RPGClient::Server = 0; + $RPGClient::PlayBooted = 0; + RPGClient_DisableOverlay(); + RPGClient_UnbindKeys(); + } +} + +function clientCmdRPG_Hud(%hp, %maxHp, %silver, %load, %cap, %skillId, %skillValue, %blocking, %tHp, %tMax) +{ + RPGClient_BootPlay(); + RPGClient_Hud_Update(%hp, %maxHp, %silver, %load, %cap, %skillId, %skillValue, %tHp, %tMax); +} + +function clientCmdRPG_HudOff() +{ + RPGClient_Hud_Hide(); +} + +function clientCmdRPG_Notify(%text, %sec) +{ + if (%text !$= "") + { + RPGClient_Notify_Show(%text, %sec); + } +} + +function clientCmdRPG_Dialog(%title, %body, %sec) +{ + RPGClient_Dialog_Show(%title, %body, %sec); +} + +function clientCmdRPG_ListBegin(%title, %page, %pages) +{ + RPGClient_List_Begin(%title, %page, %pages); +} + +function clientCmdRPG_ListRow(%row) +{ + RPGClient_List_Row(%row); +} + +function clientCmdRPG_ListEnd() +{ + RPGClient_List_End(); +} + +// --- HUD / list / dialog (Phase 4): thin windows on PlayGui --- +function RPGClient_Hud_Ensure() +{ + if (isObject(RPGClient_Hud)) + { + return; + } + new GuiTextCtrl(RPGClient_Hud) + { + horizSizing = "absolute"; + vertSizing = "absolute"; + position = "8 400"; + extent = "520 20"; + profile = "ChatHudMessageProfile"; + text = ""; + }; + PlayGui.add(RPGClient_Hud); + RPGClient_Hud_Place(); +} + +function RPGClient_Hud_Update(%hp, %maxHp, %silver, %load, %cap, %skillId, %skillValue, %tHp, %tMax) +{ + if (!$RPGClient::Active) + { + return; + } + RPGClient_Hud_Ensure(); + if (!$RPGClient::HudPlaced) + { + RPGClient_Hud_Place(); + } + %t = ""; + if (%tMax > 0) + { + %t = " | Target " @ %tHp @ "/" @ %tMax; + } + RPGClient_Hud.setText("HP " @ %hp @ "/" @ %maxHp @ %t @ " | " @ %silver @ " silver | load " @ %load @ "/" @ %cap @ " | " @ %skillId @ " " @ %skillValue); +} + +function RPGClient_Hud_Hide() +{ + if (isObject(RPGClient_Hud)) + { + RPGClient_Hud.setText(""); + } +} + +function RPGClient_Notify_Show(%text, %sec) +{ + RPGClient_Hud_Ensure(); + RPGClient_Hud.setText(%text); + if (%sec > 0) + { + schedule(%sec * 1000, RPGClient_Hud, setText, ""); + } +} + +function RPGClient_Dialog_Show(%title, %body, %sec) +{ + messageBoxOK(%title, %body, ""); +} + +function RPGClient_List_Begin(%title, %page, %pages) +{ + $RPGClient::ListBody = ""; + $RPGClient::ListTitle = %title @ " (" @ (%page + 1) @ "/" @ %pages @ ")"; +} + +function RPGClient_List_Row(%row) +{ + $RPGClient::ListBody = $RPGClient::ListBody @ %row @ "\n"; +} + +function RPGClient_List_End() +{ + if ($RPGClient::ListBody !$= "") + { + messageBoxOK($RPGClient::ListTitle, $RPGClient::ListBody, ""); + } +} + +package RPGClient_BlockMouseFire +{ + function mouseFire(%val) + { + if ($RPGClient::Active) + { + if (%val) + { + RPGClient_SendClickFromCursor(0); + } + return; + } + Parent::mouseFire(%val); + } + + function altTrigger(%val) + { + if ($RPGClient::Active) + { + if (%val) + { + RPGClient_SendClickFromCursor(1); + } + return; + } + Parent::altTrigger(%val); + } +}; +activatePackage(RPGClient_BlockMouseFire); + +package RPGClient_PageKeys +{ + function PageUpNewChatHud(%val) + { + if ($RPGClient::Active && %val) + { + RPGClient_ZoomIn(1); + return; + } + Parent::PageUpNewChatHud(%val); + } + + function PageDownNewChatHud(%val) + { + if ($RPGClient::Active && %val) + { + RPGClient_ZoomOut(1); + return; + } + Parent::PageDownNewChatHud(%val); + } +}; +activatePackage(RPGClient_PageKeys); + +package RPGClient_Input +{ + function moveforward(%val) + { + if (RPGClient_InPlay()) + { + RPGClient_KeyMvForward(%val); + return; + } + Parent::moveforward(%val); + } + + function movebackward(%val) + { + if (RPGClient_InPlay()) + { + RPGClient_KeyMvBackward(%val); + return; + } + Parent::movebackward(%val); + } + + function moveleft(%val) + { + if (RPGClient_InPlay()) + { + RPGClient_KeyMvLeft(%val); + return; + } + Parent::moveleft(%val); + } + + function moveright(%val) + { + if (RPGClient_InPlay()) + { + RPGClient_KeyMvRight(%val); + return; + } + Parent::moveright(%val); + } + + function invLeft(%val) + { + if (RPGClient_InPlay()) + { + RPGClient_YawLeft(%val); + return; + } + Parent::invLeft(%val); + } + + function invRight(%val) + { + if (RPGClient_InPlay()) + { + RPGClient_YawRight(%val); + return; + } + Parent::invRight(%val); + } + + function scrollInventory(%val) + { + if ($RPGClient::CamTickOn && %val != 0) + { + if (%val > 0) + { + $RPGClient::Dist = getMax(6, $RPGClient::Dist - 0.35 * %val); + } + else + { + $RPGClient::Dist = getMin(32, $RPGClient::Dist - 0.35 * %val); + } + RPGClient_ApplyCamera(1); + return; + } + Parent::scrollInventory(%val); + } + + function yaw(%val) + { + if ($RPGClient::CamTickOn && %val != 0) + { + $sens = $pref::Input::MouseSensitivity; + $RPGClient::Yaw += %val * %sens * 0.004; + return; + } + Parent::yaw(%val); + } +}; +activatePackage(RPGClient_Input); + +package RPGClient_MapStack +{ + function noHudGui::onWake(%this) + { + Parent::onWake(%this); + if ($RPGClient::Active) + { + schedule(0, 0, RPGClient_EnsureOverlay); + } + } +}; +activatePackage(RPGClient_MapStack); + +package RPGClient_Cursor +{ + function GuiCanvas::checkCursor(%this) + { + Parent::checkCursor(%this); + if ($RPGClient::Active && !%this.isCursorOn()) + { + lockMouse(0); + %this.cursorOn(); + } + } + + function escapeMenu::toggle(%this) + { + if (%this.isAwake()) + { + Canvas.popDialog(%this); + if ($RPGClient::Active) + { + schedule(0, 0, RPGClient_EnsureCursor); + schedule(0, 0, RPGClient_EnsureOverlay); + } + return; + } + if (Canvas.getContent().getName() $= "LoadingGui") + { + disconnect(); + return; + } + Canvas.pushDialog(%this); + } +}; +activatePackage(RPGClient_Cursor); + +package RPGClient_ViewLock +{ + function toggleFirstPerson(%val) + { + if ($RPGClient::Active) + { + return; + } + Parent::toggleFirstPerson(%val); + } + + function toggleFreeLook(%val) + { + if ($RPGClient::Active) + { + return; + } + Parent::toggleFreeLook(%val); + } + + function dropCameraAtPlayer(%val) + { + if ($RPGClient::Active) + { + return; + } + Parent::dropCameraAtPlayer(%val); + } +}; +activatePackage(RPGClient_ViewLock); + +package RPGClient_Cleanup +{ + function disconnectedCleanup(%doReconnect) + { + RPGClient_DisableOverlay(); + RPGClient_UnbindKeys(); + $RPGClient::Server = 0; + $RPGClient::HelloPending = 0; + $RPGClient::HelloLast = ""; + cancel($RPGClient::TopDownWatchSched); + $RPGClient::TopDownWatchSched = 0; + $RPGClient::Server = 0; + $RPGClient::PlayBooted = 0; + $RPGClient::CamTickOn = 0; + RPGClient_ClearGlobalKeys(); + RPGClient_RestoreMoveMap(); + Parent::disconnectedCleanup(%doReconnect); + } +}; +activatePackage(RPGClient_Cleanup); + +package RPGClient_Mission +{ + function onMissionDownloadComplete() + { + Parent::onMissionDownloadComplete(); + schedule(250, 0, RPGClient_SendHello); + } +}; +activatePackage(RPGClient_Mission); + +echo("Client_RPG loaded (protocol " @ $RPGClient::Version @ ")"); diff --git a/description.txt b/description.txt index 295e9a2..18ffa94 100644 --- a/description.txt +++ b/description.txt @@ -1,2 +1,2 @@ -Client_RPG -Required client add-on for the GameMode_RPG realm: top-down camera, click-to-move and combat, and the RPG windows. Without it you can only spectate on an RPG server. +Client_RPG +Required client add-on for the GameMode_RPG realm: top-down camera, click-to-move and combat, and the RPG windows. Without it you can only spectate on an RPG server. diff --git a/ui.cs b/ui.cs index cc75ddd..848363e 100644 --- a/ui.cs +++ b/ui.cs @@ -1,72 +1,72 @@ -// Client windows: hotkeys open /rpg lists; the server still validates every command. - -$RPGClient::MenuOpen = 0; - -function RPGClient_RunCmd(%cmd) -{ - if (!isObject(ServerConnection)) - { - return; - } - commandToServer('Message', "/rpg " @ %cmd); -} - -function RPGClient_ToggleMenu(%name, %cmd) -{ - if ($RPGClient::MenuOpen $= %name) - { - $RPGClient::MenuOpen = ""; - return; - } - $RPGClient::MenuOpen = %name; - RPGClient_RunCmd(%cmd); -} - -function RPGClient_Menu_Bag() -{ - RPGClient_ToggleMenu("bag", "bag"); -} - -function RPGClient_Menu_Skills() -{ - RPGClient_ToggleMenu("skills", "skills"); -} - -function RPGClient_Menu_Equip() -{ - RPGClient_ToggleMenu("equip", "equip"); -} - -function RPGClient_Menu_Help() -{ - RPGClient_RunCmd("help"); -} - -function RPGClient_Menu_Retaliate() -{ - RPGClient_RunCmd("retaliate"); -} - -function RPGClient_BindKeys() -{ - if (isObject(RPGClient_MenuMap)) - { - RPGClient_MenuMap.delete(); - } - new ActionMap(RPGClient_MenuMap); - RPGClient_MenuMap.bind(keyboard0, b, RPGClient_Menu_Bag); - RPGClient_MenuMap.bind(keyboard0, k, RPGClient_Menu_Skills); - RPGClient_MenuMap.bind(keyboard0, e, RPGClient_Menu_Equip); - RPGClient_MenuMap.bind(keyboard0, f1, RPGClient_Menu_Help); - RPGClient_MenuMap.bind(keyboard0, r, RPGClient_Menu_Retaliate); - RPGClient_MenuMap.push(); -} - -function RPGClient_UnbindKeys() -{ - if (isObject(RPGClient_MenuMap)) - { - RPGClient_MenuMap.pop(); - RPGClient_MenuMap.delete(); - } -} +// Client windows: hotkeys open /rpg lists; the server still validates every command. + +$RPGClient::MenuOpen = 0; + +function RPGClient_RunCmd(%cmd) +{ + if (!isObject(ServerConnection)) + { + return; + } + commandToServer('Message', "/rpg " @ %cmd); +} + +function RPGClient_ToggleMenu(%name, %cmd) +{ + if ($RPGClient::MenuOpen $= %name) + { + $RPGClient::MenuOpen = ""; + return; + } + $RPGClient::MenuOpen = %name; + RPGClient_RunCmd(%cmd); +} + +function RPGClient_Menu_Bag() +{ + RPGClient_ToggleMenu("bag", "bag"); +} + +function RPGClient_Menu_Skills() +{ + RPGClient_ToggleMenu("skills", "skills"); +} + +function RPGClient_Menu_Equip() +{ + RPGClient_ToggleMenu("equip", "equip"); +} + +function RPGClient_Menu_Help() +{ + RPGClient_RunCmd("help"); +} + +function RPGClient_Menu_Retaliate() +{ + RPGClient_RunCmd("retaliate"); +} + +function RPGClient_BindKeys() +{ + if (isObject(RPGClient_MenuMap)) + { + RPGClient_MenuMap.delete(); + } + new ActionMap(RPGClient_MenuMap); + RPGClient_MenuMap.bind(keyboard0, b, RPGClient_Menu_Bag); + RPGClient_MenuMap.bind(keyboard0, k, RPGClient_Menu_Skills); + RPGClient_MenuMap.bind(keyboard0, e, RPGClient_Menu_Equip); + RPGClient_MenuMap.bind(keyboard0, f1, RPGClient_Menu_Help); + RPGClient_MenuMap.bind(keyboard0, r, RPGClient_Menu_Retaliate); + RPGClient_MenuMap.push(); +} + +function RPGClient_UnbindKeys() +{ + if (isObject(RPGClient_MenuMap)) + { + RPGClient_MenuMap.pop(); + RPGClient_MenuMap.delete(); + } +}