|
| 1 | +#include "CinematicCommon.as" |
| 2 | +#include "MapVotesCommon.as" |
| 3 | + |
| 4 | +#define CLIENT_ONLY |
| 5 | + |
| 6 | +const bool FOCUS_ON_IMPORTANT_BLOBS = true; //whether camera should focus on important blobs |
| 7 | + |
| 8 | +const float CINEMATIC_PAN_X_EASE = 6.0f; //amount of ease along the x-axis while cinematic |
| 9 | +const float CINEMATIC_PAN_Y_EASE = 6.0f; //amount of ease along the y-axis while cinematic |
| 10 | + |
| 11 | +const float CINEMATIC_ZOOM_EASE = 16.0f; //amount of ease when zooming while cinematic |
| 12 | +const float CINEMATIC_CLOSEST_ZOOM = 1.0f; //how close the camera can zoom in while cinematic (default is 2.0f) |
| 13 | +const float CINEMATIC_FURTHEST_ZOOM = 0.5f; //how far the camera can zoom out while cinematic (default is 0.5f) |
| 14 | + |
| 15 | +const float AUTO_CINEMATIC_TIME = 3.0f; //time until camera automatically becomes cinematic. set to zero to disable |
| 16 | + |
| 17 | +Vec2f posActual; |
| 18 | +Vec2f posTarget; //position which cinematic camera moves towards |
| 19 | +float zoomTarget = 1.0f; //zoom level which camera zooms towards |
| 20 | +float timeToScroll = 0.0f; //time until next able to scroll to zoom camera |
| 21 | +float timeToCinematic = 0.0f; //time until camera automatically becomes cinematic |
| 22 | +float panEaseModifier = 1.0f; //by how much the x/y ease values are multiplied |
| 23 | +float zoomEaseModifier = 1.0f; //by how much the zoom ease values are multiplied |
| 24 | +uint currentTarget; //the current target blob |
| 25 | +uint switchTarget; //time when camera can move onto new target |
| 26 | + |
| 27 | +bool justClicked = false; |
| 28 | +string _targetPlayer; |
| 29 | +bool waitForRelease = false; |
| 30 | + |
| 31 | +CPlayer@ targetPlayer() |
| 32 | +{ |
| 33 | + return getPlayerByUsername(_targetPlayer); |
| 34 | +} |
| 35 | + |
| 36 | +const Vec2f[] easePosLerpTable = { |
| 37 | + Vec2f(0.0, 1.0), |
| 38 | + Vec2f(8.0, 1.0), |
| 39 | + Vec2f(16.0, 0.8), |
| 40 | + Vec2f(64.0, 0.6), |
| 41 | + Vec2f(96.0, 0.8), |
| 42 | + Vec2f(128.0, 1.0), |
| 43 | +}; |
| 44 | + |
| 45 | +float ease(float current, float target, float factor) |
| 46 | +{ |
| 47 | + const float diff = target - current; |
| 48 | + const float linearCorrection = diff * factor * panEaseModifier; |
| 49 | + |
| 50 | + const float x = Maths::Abs(diff); |
| 51 | + |
| 52 | + float cubicCorrectionMod = 1.0; |
| 53 | + for (int i = 1; i < easePosLerpTable.size(); ++i) |
| 54 | + { |
| 55 | + Vec2f a = easePosLerpTable[i-1]; |
| 56 | + Vec2f b = easePosLerpTable[i]; |
| 57 | + if (x >= a.x && x < b.x) |
| 58 | + { |
| 59 | + const float f = (x - a.x) / (b.x - a.x); |
| 60 | + cubicCorrectionMod = Maths::Lerp(a.y, b.y, f); |
| 61 | + break; |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + const float finalCorrection = linearCorrection * cubicCorrectionMod; |
| 66 | + |
| 67 | + return current + linearCorrection * cubicCorrectionMod; |
| 68 | +} |
| 69 | + |
| 70 | +void SetTargetPlayer(CPlayer@ p) |
| 71 | +{ |
| 72 | + _targetPlayer = ""; |
| 73 | + if (p is null) return; |
| 74 | + _targetPlayer = p.getUsername(); |
| 75 | +} |
| 76 | + |
| 77 | +void Spectator(CRules@ this) |
| 78 | +{ |
| 79 | + CCamera@ camera = getCamera(); |
| 80 | + CControls@ controls = getControls(); |
| 81 | + CMap@ map = getMap(); |
| 82 | + |
| 83 | + if (camera is null || controls is null || map is null) |
| 84 | + { |
| 85 | + return; |
| 86 | + } |
| 87 | + |
| 88 | + //variables |
| 89 | + Vec2f mapDim = map.getMapDimensions(); |
| 90 | + float camSpeed = getRenderApproximateCorrectionFactor() * 15.0f / zoomTarget; |
| 91 | + |
| 92 | + if (this.get_bool("set new target")) |
| 93 | + { |
| 94 | + string newTarget = this.get_string("new target"); |
| 95 | + _targetPlayer = newTarget; |
| 96 | + if (targetPlayer() !is null) |
| 97 | + { |
| 98 | + waitForRelease = true; |
| 99 | + this.set_bool("set new target", false); |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + //scroll to zoom |
| 104 | + if (timeToScroll <= 0) |
| 105 | + { |
| 106 | + if (controls.isKeyJustPressed(controls.getActionKeyKey(AK_ZOOMIN))) |
| 107 | + { |
| 108 | + timeToScroll = 7; |
| 109 | + setCinematicEnabled(false); |
| 110 | + |
| 111 | + if (zoomTarget < 1.0f) |
| 112 | + { |
| 113 | + zoomTarget = 1.0f; |
| 114 | + } |
| 115 | + else |
| 116 | + { |
| 117 | + zoomTarget = 2.0f; |
| 118 | + } |
| 119 | + } |
| 120 | + else if (controls.isKeyJustPressed(controls.getActionKeyKey(AK_ZOOMOUT))) |
| 121 | + { |
| 122 | + timeToScroll = 7; |
| 123 | + setCinematicEnabled(false); |
| 124 | + |
| 125 | + if (zoomTarget > 1.0f) |
| 126 | + { |
| 127 | + zoomTarget = 1.0f; |
| 128 | + } |
| 129 | + else |
| 130 | + { |
| 131 | + zoomTarget = 0.5f; |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + else |
| 136 | + { |
| 137 | + timeToScroll -= getRenderApproximateCorrectionFactor(); |
| 138 | + } |
| 139 | + |
| 140 | + //move camera using action movement keys |
| 141 | + if (controls.ActionKeyPressed(AK_MOVE_LEFT)) |
| 142 | + { |
| 143 | + posActual.x -= camSpeed; |
| 144 | + SetTargetPlayer(null); |
| 145 | + setCinematicEnabled(false); |
| 146 | + } |
| 147 | + if (controls.ActionKeyPressed(AK_MOVE_RIGHT)) |
| 148 | + { |
| 149 | + posActual.x += camSpeed; |
| 150 | + SetTargetPlayer(null); |
| 151 | + setCinematicEnabled(false); |
| 152 | + } |
| 153 | + if (controls.ActionKeyPressed(AK_MOVE_UP)) |
| 154 | + { |
| 155 | + posActual.y -= camSpeed; |
| 156 | + SetTargetPlayer(null); |
| 157 | + setCinematicEnabled(false); |
| 158 | + } |
| 159 | + if (controls.ActionKeyPressed(AK_MOVE_DOWN)) |
| 160 | + { |
| 161 | + posActual.y += camSpeed; |
| 162 | + SetTargetPlayer(null); |
| 163 | + setCinematicEnabled(false); |
| 164 | + } |
| 165 | + |
| 166 | + if (controls.isKeyJustReleased(KEY_LBUTTON)) |
| 167 | + { |
| 168 | + waitForRelease = false; |
| 169 | + } |
| 170 | + |
| 171 | + if (!isCinematicEnabled() || targetPlayer() !is null) //player-controlled zoom |
| 172 | + { |
| 173 | + if (Maths::Abs(camera.targetDistance - zoomTarget) > 0.001f) |
| 174 | + { |
| 175 | + camera.targetDistance = (camera.targetDistance * (3.0f - getRenderApproximateCorrectionFactor() + 1.0f) + (zoomTarget * getRenderApproximateCorrectionFactor())) / 4.0f; |
| 176 | + } |
| 177 | + else |
| 178 | + { |
| 179 | + camera.targetDistance = zoomTarget; |
| 180 | + } |
| 181 | + |
| 182 | + if (AUTO_CINEMATIC_TIME > 0) |
| 183 | + { |
| 184 | + timeToCinematic -= getRenderSmoothDeltaTime(); |
| 185 | + if (timeToCinematic <= 0) |
| 186 | + { |
| 187 | + setCinematicEnabled(true); |
| 188 | + } |
| 189 | + } |
| 190 | + } |
| 191 | + else //cinematic camera |
| 192 | + { |
| 193 | + const float corrFactor = getRenderApproximateCorrectionFactor(); |
| 194 | + camera.targetDistance += (zoomTarget - camera.targetDistance) / CINEMATIC_ZOOM_EASE * corrFactor * zoomEaseModifier; |
| 195 | + |
| 196 | + posActual.x = ease(posActual.x, posTarget.x, corrFactor / CINEMATIC_PAN_X_EASE); |
| 197 | + posActual.y = ease(posActual.y, posTarget.y, corrFactor / CINEMATIC_PAN_Y_EASE); |
| 198 | + } |
| 199 | + |
| 200 | + //click on players to track them or set camera to mousePos |
| 201 | + Vec2f mousePos = controls.getMouseWorldPos(); |
| 202 | + if (controls.isKeyJustPressed(KEY_LBUTTON) && !waitForRelease) |
| 203 | + { |
| 204 | + CBlob@[] players; |
| 205 | + SetTargetPlayer(null); |
| 206 | + getBlobsByTag("player", @players); |
| 207 | + for (uint i = 0; i < players.length; i++) |
| 208 | + { |
| 209 | + CBlob@ blob = players[i]; |
| 210 | + Vec2f bpos = blob.getInterpolatedPosition(); |
| 211 | + if (blob.getName() == "migrant") //screw migrants |
| 212 | + { |
| 213 | + continue; |
| 214 | + } |
| 215 | + |
| 216 | + if (Maths::Pow(mousePos.x - bpos.x, 2) + Maths::Pow(mousePos.y - bpos.y, 2) <= Maths::Pow(blob.getRadius() * 2, 2) && camera.getTarget() !is blob) |
| 217 | + { |
| 218 | + SetTargetPlayer(blob.getPlayer()); |
| 219 | + camera.setTarget(blob); |
| 220 | + waitForRelease = true; |
| 221 | + setCinematicEnabled(false); |
| 222 | + } |
| 223 | + } |
| 224 | + } |
| 225 | + else if (!waitForRelease && controls.isKeyPressed(KEY_LBUTTON) && camera.getTarget() is null) //classic-like held mouse moving |
| 226 | + { |
| 227 | + // HACK: this is terrible and we need proper GUI and cursor capture shit |
| 228 | + // ofc this is still an issue with the queue stuff now :upside_down: |
| 229 | + MapVotesMenu@ mvm = null; |
| 230 | + this.get("MapVotesMenu", @mvm); |
| 231 | + |
| 232 | + if (mvm is null || !isMapVoteActive() || !mvm.screenPositionOverlaps(controls.getMouseScreenPos())) |
| 233 | + { |
| 234 | + posActual += (mousePos - posActual) / 8.0f * getRenderApproximateCorrectionFactor(); |
| 235 | + setCinematicEnabled(false); |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + if (targetPlayer() !is null) |
| 240 | + { |
| 241 | + if (camera.getTarget() !is targetPlayer().getBlob()) |
| 242 | + { |
| 243 | + camera.setTarget(targetPlayer().getBlob()); |
| 244 | + } |
| 245 | + posActual = camera.getPosition(); |
| 246 | + } |
| 247 | + else |
| 248 | + { |
| 249 | + camera.setTarget(null); |
| 250 | + } |
| 251 | + |
| 252 | + //set specific zoom if we have a target |
| 253 | + if (camera.getTarget() !is null) |
| 254 | + { |
| 255 | + camera.mousecamstyle = 1; |
| 256 | + camera.mouseFactor = 0.5f; |
| 257 | + return; |
| 258 | + } |
| 259 | + |
| 260 | + //keep camera within map boundaries |
| 261 | + float borderMarginX = map.tilesize * 2.0f / zoomTarget; |
| 262 | + float borderMarginY = map.tilesize * 2.0f / zoomTarget; |
| 263 | + posActual.x = Maths::Clamp(posActual.x, borderMarginX, mapDim.x - borderMarginX); |
| 264 | + posActual.y = Maths::Clamp(posActual.y, borderMarginY, mapDim.y - borderMarginY); |
| 265 | + |
| 266 | + //set camera position |
| 267 | + camera.setPosition(posActual); |
| 268 | +} |
0 commit comments