szymski

Untitled

May 22nd, 2022
65
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.37 KB | None | 0 0
  1. RACING2.RaceStates = {
  2. Waiting = 0,
  3. Countdown = 1,
  4. Racing = 2,
  5. Finished = 3,
  6. };
  7.  
  8. var MAX_LEAVE_COUNT = 1;
  9.  
  10. /*
  11. Only data or functions?
  12. */
  13. class RACING2_RacePlayerContext {
  14. var Player;
  15. var VehicleProvider;
  16. var VehicleId;
  17. var PlayerInitialPosition;
  18.  
  19. var Vehicle;
  20. var OwnVehicle = false; /// True if the vehicle belongs to the player.
  21. var VehicleInitialPosition;
  22.  
  23. var PointIndex = 1;
  24.  
  25. var Distance = 0;
  26. var Time;
  27.  
  28. var LeftVehicleCount = 0;
  29.  
  30. var Cleared = false;
  31.  
  32. function this(ply, vehicleProvider, vehicleId) {
  33. this.VehicleProvider = vehicleProvider;
  34. this.VehicleId = vehicleId;
  35. this.Player = ply;
  36. }
  37. }
  38.  
  39. /**
  40. Base class for all races. Each race type has its own class.
  41. */
  42. class RACING2_Race {
  43.  
  44. /// String identifier of the race. Used in timers.
  45. var Id;
  46.  
  47. var SpawnedTrack;
  48. var Track;
  49.  
  50. var Players = { };
  51. var CancelledPlayers = { };
  52. /// Player is the key.
  53. var PlayerContexts = { };
  54. var CancelledContexts = { };
  55. var ContextByVehicle = { };
  56.  
  57. var State = RACING2.RaceStates.Waiting;
  58.  
  59. /// When the countdown ends
  60. var NextStart;
  61. /// CurTime() when the race starts (after the countdown).
  62. var StartTime;
  63.  
  64. var Demo;
  65.  
  66. function this(spawnedTrack) {
  67. this.SpawnedTrack = spawnedTrack;
  68. this.Track = spawnedTrack.Track;
  69. this.Id = "" .. this.Track.Id .. "_";
  70.  
  71. this.Track:CachePoints();
  72.  
  73. this.Demo = RACING2_Demo(this.Track);
  74. }
  75.  
  76. function AddPlayer(ply, vehicleProvider, vehicleId) {
  77. assert(IsValid(ply), "Player is invalid.");
  78.  
  79. this.Players[#this.Players + 1] = ply;
  80.  
  81. var context = RACING2_RacePlayerContext(ply, vehicleProvider, vehicleId);
  82. context.IsBot = ply:IsBot();
  83. this.PlayerContexts[ply] = context;
  84. }
  85.  
  86. function Start() {
  87. foreach(var context in this.PlayerContexts) {
  88. context.PlayerInitialPosition = context.Player:GetPos();
  89. }
  90.  
  91. this:CreateTimers();
  92. this:CreateHooks();
  93.  
  94. this.NextStart = CurTime() + RACING2Config.CountdownSeconds;
  95.  
  96. RACING2.Networking.Race_Begin(this);
  97.  
  98. this.Demo:TrackEvent("start");
  99. }
  100.  
  101. /**
  102. Spawns a vehicle for the player base on the provider,
  103. sets its position and assigns it to the context.
  104. */
  105. function SpawnVehicleForPlayer(ply, index) {
  106. // TODO: Cancel the race if vehicle can't be spawned
  107.  
  108. var provider = this.PlayerContexts[ply].VehicleProvider;
  109.  
  110. var initialPosition = this.Track:GetVehiclePosition(index);
  111. var initialAngles = this.Track:GetDirectionFromPoint(1):Angle() + Angle(0, -90, 0);
  112.  
  113. // No provider - use predefined provider
  114. if(!provider || provider == "")
  115. provider = RACING2.VehicleProviders["predefined"];
  116.  
  117. var vehicle = provider:GetVehicleEntity(this.Track, this.PlayerContexts[ply].VehicleId, ply);
  118.  
  119. if(!IsValid(vehicle)) {
  120. this:CancelRace();
  121. return;
  122. }
  123.  
  124. vehicle:SetPos(initialPosition);
  125. vehicle:SetAngles(initialAngles);
  126.  
  127. this.PlayerContexts[ply].Vehicle = vehicle;
  128. this.PlayerContexts[ply].VehicleInitialPosition = initialPosition;
  129.  
  130. return vehicle;
  131. }
  132.  
  133. function Destroy() {
  134. this.SpawnedTrack.Race = null;
  135.  
  136. this:RemoveTimers();
  137. this:RemoveHooks();
  138.  
  139. foreach(var context in this.PlayerContexts)
  140. this:ClearPlayer(context.Player);
  141.  
  142. foreach(var context in this.CancelledContexts)
  143. this:ClearPlayer(context.Player);
  144.  
  145. RACING2.Networking.Race_End(this);
  146. }
  147.  
  148. /*----------------------------
  149. Timers
  150. ------------------------------*/
  151.  
  152. function CreateTimers() {
  153. RACING2.Timer.Create(this.Id .. "recordDemo", 0.1, 0, function() {
  154. foreach(var context in this.PlayerContexts)
  155. this.Demo:TrackVehicle(context.Player, context.Vehicle);
  156. });
  157. }
  158.  
  159. function RemoveTimers() {
  160. RACING2.Timer.Remove(this.Id .. "updateRacing");
  161. RACING2.Timer.Remove(this.Id .. "wonRace");
  162. RACING2.Timer.Remove(this.Id .. "countdown");
  163. RACING2.Timer.Remove(this.Id .. "recordDemo");
  164. }
  165.  
  166. /*----------------------------
  167. Hooks
  168. ------------------------------*/
  169.  
  170. function CreateHooks() {
  171. RACING2.Hook.Add("PlayerLeaveVehicle", this.Id .. "leaveVehicle", function(ply, vehicle) {
  172. var context = this.PlayerContexts[ply];
  173. if(context && context.Vehicle == vehicle)
  174. this:PlayerLeaveVehicle(ply, context);
  175. });
  176.  
  177. RACING2.Hook.Add("PlayerDisconnected", this.Id .. "disconnect", function(ply) {
  178. var context = this.PlayerContexts[ply];
  179. if(context)
  180. this:PlayerDisconnected(ply, context);
  181. });
  182.  
  183. // For recording demo sounds
  184. RACING2.Hook.Add("EntityEmitSound", this.Id .. "emitSound", function(tbl) {
  185. var context = this.ContextByVehicle[tbl.Entity];
  186. if(context)
  187. this.Demo:TrackSound(context.Player, tbl);
  188. });
  189. }
  190.  
  191. function RemoveHooks() {
  192. RACING2.Hook.Remove("PlayerLeaveVehicle", this.Id .. "leaveVehicle");
  193. RACING2.Hook.Remove("PlayerDisconnected", this.Id .. "disconnect");
  194. RACING2.Hook.Remove("Think", this.Id .. "freezeVehicles");
  195. RACING2.Hook.Remove("EntityEmitSound", this.Id .. "emitSound");
  196. }
  197.  
  198. function SpawnVehicle(name, position, angle) {
  199. var tbl = list.Get("Vehicles")[name];
  200.  
  201. var vehicle = ents.Create(tbl.Class);
  202. vehicle:SetPos(position);
  203. vehicle:SetAngles(angle);
  204. vehicle:SetModel(tbl.Model);
  205. foreach(var key, value in tbl.KeyValues)
  206. vehicle:SetKeyValue(key, value);
  207. vehicle:Spawn();
  208.  
  209. return vehicle;
  210. }
  211.  
  212. /*----------------------------
  213. Countdown state
  214. ------------------------------*/
  215.  
  216. /**
  217. Isn't called automatically by Start.
  218. Players are in their vehicles now.
  219. */
  220. function BeginCountdown() {
  221. foreach(var context in this.PlayerContexts) {
  222. this.ContextByVehicle[context.Vehicle] = context;
  223. this.Demo:AddPlayer(context.Player, context.Vehicle);
  224. }
  225.  
  226. this.State = RACING2.RaceStates.Countdown;
  227. this:CreateVehicleFreezeHook();
  228.  
  229. var countdownSeconds = RACING2Config.CountdownSeconds;
  230.  
  231. RACING2.Timer.Create(this.Id .. "countdown", 1, countdownSeconds, function() {
  232. countdownSeconds--;
  233.  
  234. foreach(var context in this.PlayerContexts)
  235. RACING2.ChatMessage(context.Player, "" .. countdownSeconds);
  236.  
  237. if(countdownSeconds == 0) {
  238. this:RemoveVehicleFreezeHook();
  239. this:BeginRacing();
  240. }
  241. });
  242. }
  243.  
  244. function CreateVehicleFreezeHook() {
  245. RACING2.Hook.Add("Think", this.Id .. "freezeVehicles", function() {
  246. foreach(var context in this.PlayerContexts) {
  247. if(IsValid(context.Vehicle))
  248. context.Vehicle:SetPos(context.VehicleInitialPosition);
  249. }
  250. });
  251. }
  252.  
  253. function RemoveVehicleFreezeHook() {
  254. RACING2.Hook.Remove("Think", this.Id .. "freezeVehicles");
  255. }
  256.  
  257. /*----------------------------
  258. Racing state
  259. ------------------------------*/
  260.  
  261. /**
  262. Called automatically by countdown timer.
  263. */
  264. function BeginRacing() {
  265. this.State = RACING2.RaceStates.Racing;
  266. this.StartTime = CurTime();
  267.  
  268. // UpdateRacing timer
  269. RACING2.Timer.Create(this.Id .. "updateRacing", 0.1, 0, function() { this:UpdateRacing(); });
  270.  
  271. RACING2.Networking.Race_Racing(this);
  272.  
  273. this.Demo:TrackEvent("racing");
  274. }
  275.  
  276. function UpdateRacing() {
  277. if(this.State != RACING2.RaceStates.Racing)
  278. return;
  279.  
  280. foreach(var context in this.PlayerContexts) {
  281. if(context.PointIndex == #this.Track.CachedPoints)
  282. continue;
  283.  
  284. this:UpdateDistance(context);
  285. this:UpdatePoints(context);
  286.  
  287. if(context.IsBot)
  288. this:UpdateBot(context);
  289. }
  290. }
  291.  
  292. function UpdatePoints(context) {
  293. if(context.PointIndex >= #this.Track.CachedPoints)
  294. return;
  295.  
  296. var curPoint = this.Track.CachedPoints[context.PointIndex];
  297. var nextPoint = this.Track.CachedPoints[context.PointIndex + 1];
  298.  
  299. var direction = this.Track:GetDirectionFromCachedPoint(context.PointIndex);
  300. var directionFromNextToPlayer = (context.Vehicle:GetPos() - nextPoint);
  301.  
  302. var dotProduct = direction:Dot(directionFromNextToPlayer);
  303.  
  304. // We've reached the point, update the index
  305. if(dotProduct > 0) {
  306. context.PointIndex = context.PointIndex + 1;
  307.  
  308. if(context.PointIndex == #this.Track.CachedPoints)
  309. this:ReachedLastPoint(context);
  310. //else
  311. // RACING2.ChatMessage(context.Player, "Damn! Go to the next one!");
  312.  
  313. RACING2.Networking.Race_Point(context.Player, context.PointIndex);
  314.  
  315. this:UpdateScoreboard();
  316. }
  317. }
  318.  
  319. function UpdateDistance(context) {
  320. var distance = 0;
  321.  
  322. for(var i = 1; i <= context.PointIndex; i++) {
  323. var point = this.Track.CachedPoints[i];
  324. var nextPoint = this.Track.CachedPoints[i + 1];
  325.  
  326. distance += (point - nextPoint):Length();
  327. }
  328.  
  329. if(context.PointIndex != #this.Track.CachedPoints)
  330. distance -= (context.Vehicle:GetPos() - this.Track.CachedPoints[context.PointIndex + 1]):Length();
  331.  
  332. context.Distance = distance;
  333. }
  334.  
  335. var LastScoreboard = { };
  336.  
  337. function UpdateScoreboard() {
  338. var scoreboard = { };
  339. foreach(var context in this.PlayerContexts)
  340. scoreboard[#scoreboard + 1] = context.Player;
  341.  
  342. table.sort(scoreboard, function(a, b) {
  343. return this.PlayerContexts[a].PointIndex > this.PlayerContexts[b].PointIndex;
  344. });
  345.  
  346. var shouldUpdate = false;
  347. foreach(var k, v in scoreboard) {
  348. if(this.LastScoreboard[k] != v) {
  349. shouldUpdate = true;
  350. break;
  351. }
  352. }
  353.  
  354. if(shouldUpdate) {
  355. RACING2.Networking.Race_Scoreboard(this, scoreboard);
  356. this.LastScoreboard = scoreboard;
  357. }
  358. }
  359.  
  360. // TODO: Multiple laps
  361. function ReachedLastPoint(context) {
  362. this:PlayerWon(context);
  363. }
  364.  
  365. function PlayerWon(context) {
  366. context.Time = CurTime() - this.StartTime;
  367.  
  368. this.WinnerPlayer = context.Player;
  369.  
  370. this:BeginFinished();
  371. }
  372.  
  373. function UpdateBot(context) {
  374. var ply = context.Player;
  375. var vehicle = context.Vehicle;
  376. var po = vehicle:GetPhysicsObject();
  377.  
  378. po:ApplyForceCenter(vehicle:GetForward() * po:GetMass() * 40);
  379.  
  380. var curPoint = this.Track.CachedPoints[context.PointIndex];
  381. var nextPoint = this.Track.CachedPoints[context.PointIndex + 1];
  382.  
  383. if(!nextPoint) {
  384. curPoint = this.Track.CachedPoints[context.PointIndex - 1];
  385. nextPoint = this.Track.CachedPoints[context.PointIndex];
  386. }
  387.  
  388. var diff = (nextPoint - curPoint);
  389.  
  390. //var ang = diff:Angle().y - vehicle:GetAngles().y;
  391. var dAng = (diff):Angle();
  392. dAng.p = 0;
  393. dAng.r = 0;
  394. dAng.y = dAng.y - 90;
  395. var ang = vehicle:WorldToLocalAngles(dAng);
  396.  
  397. po:AddAngleVelocity(Vector(0, 0, ang.y * 15));
  398. //vehicle:SetAngles(dAng);
  399. }
  400.  
  401. function CancelPlayer(ply) {
  402. assert(this.PlayerContexts[ply], "That player has already been cancelled or didn't take part in the race.");
  403.  
  404. var context = this.PlayerContexts[ply];
  405.  
  406. // Must be here, because this.Players is used.
  407. RACING2.Networking.Race_PlayerCancelled(this, ply);
  408.  
  409. this.CancelledContexts[ply] = this.PlayerContexts[ply];
  410. table.RemoveByValue(this.Players, ply);
  411. this.PlayerContexts[ply] = null;
  412. this.CancelledPlayers[#this.CancelledPlayers + 1] = ply;
  413.  
  414. RACING2.ChatMessage(ply, RACING2.Language["The race has been cancelled for you."]);
  415.  
  416. this:ClearPlayer(context.Player);
  417.  
  418. this:PlayerCancelled(this.CancelledContexts[ply]);
  419. }
  420.  
  421. function PlayerCancelled(context) {
  422. if(this.State == RACING2.RaceStates.Countdown) {
  423. RACING2.ChatMessage(this.Players, RACING2.Language["Someone left, cancelling the race."]);
  424. this:CancelRace();
  425. return;
  426. }
  427.  
  428. if(#this.Players == 0)
  429. this:CancelRace();
  430. }
  431.  
  432. function CancelRace() {
  433. this:RaceCancelled();
  434. this:RaceEnded();
  435. this:Destroy();
  436. }
  437.  
  438. function RaceCancelled() {
  439. RACING2.ChatMessage(player.GetAll()[1], "The race has been cancelled.");
  440. }
  441.  
  442. function ClearPlayer(ply) {
  443. var context = this.PlayerContexts[ply] ?? this.CancelledContexts[ply];
  444. assert(context, "No such player in the race.");
  445.  
  446. if(context.Cleared)
  447. return;
  448.  
  449. context.Cleared = true;
  450.  
  451. if(!context.OwnVehicle && IsValid(context.Vehicle))
  452. context.Vehicle:Remove();
  453. }
  454.  
  455. /**
  456. Called when the player taking a part in the race left their vehicle.
  457. Makes them enter the vehicle again a few times, to prevent cancelling
  458. the race by accident.
  459. */
  460. function PlayerLeaveVehicle(ply, context) {
  461. context.LeftVehicleCount = context.LeftVehicleCount + 1;
  462.  
  463. if(context.LeftVehicleCount >= MAX_LEAVE_COUNT) {
  464. this:CancelPlayer(ply);
  465. return;
  466. }
  467.  
  468. RACING2.ChatMessage(ply, string.format(RACING2.Language["You left your vehicle %s times. If you leave %s times you will be kicked out of the race."], context.LeftVehicleCount, MAX_LEAVE_COUNT));
  469.  
  470. var hookId = this.Track.Id .. "_ForceEnterVehicle" .. ply:EntIndex();
  471.  
  472. RACING2.Hook.Add("Think", hookId, function() {
  473. if(ply:GetVehicle() == context.Vehicle || !IsValid(ply)) {
  474. RACING2.Hook.Remove("Think", hookId);
  475. return;
  476. }
  477.  
  478. ply:EnterVehicle(context.Vehicle);
  479. });
  480. }
  481.  
  482. function PlayerDisconnected(ply, context) {
  483. this:CancelPlayer(ply);
  484. }
  485.  
  486. /*----------------------------
  487. Finished state
  488. ------------------------------*/
  489.  
  490. function BeginFinished() {
  491. this.State = RACING2.RaceStates.Finished;
  492. RACING2.Timer.Remove(this.Id .. "updateRacing");
  493.  
  494. foreach(var c in this.PlayerContexts) {
  495. RACING2.ChatMessage(c.Player, string.format(RACING2.Language["%s has won the race!"], this.WinnerPlayer:Name()));
  496. }
  497.  
  498. var winnerContext = this.PlayerContexts[this.WinnerPlayer];
  499.  
  500. RACING2.ChatMessage(this.WinnerPlayer, "You damn won! GG");
  501. RACING2.ChatMessage(this.WinnerPlayer, "Time: " .. winnerContext.Time);
  502.  
  503. RACING2.Networking.Race_PlayerFinished(this.WinnerPlayer, winnerContext.Time);
  504.  
  505. RACING2.Timer.Create(this.Id .. "wonRace", 5, 1, function() {
  506. this:RaceEnded();
  507. this:Destroy();
  508. });
  509. }
  510.  
  511. // Called both when race finishes and is being cancelled.
  512. function RaceEnded() {
  513. this.Demo:TrackEvent("end");
  514. }
  515. }
  516.  
  517. RACING2.Hook.Add("RACING2_RegisterRaceTypes", "register_race_race", function() {
  518. RACING2.RegisterRaceConstructor("race", RACING2_Race);
  519. });
  520.  
  521. /*----------------------------
  522. Debug race
  523. ------------------------------*/
  524.  
  525. class RACING2_Race_Test : RACING2_Race {
  526. function this(spawnedTrack) {
  527. super(spawnedTrack);
  528. }
  529.  
  530. function Start() {
  531. super();
  532.  
  533. foreach(var i, ply in this.Players) {
  534. var vehicle = this:SpawnVehicleForPlayer(ply, i);
  535. ply:EnterVehicle(vehicle);
  536. }
  537.  
  538. // foreach(var i, ply in this.Players) {
  539. // // TODO: Later move this to the base class
  540. // var vehicle = this:CreateDebugVehicle(this.Track:GetVehiclePosition(i),
  541. // this.Track:GetDirectionFromPoint(1):Angle() + Angle(0, -90, 0));
  542.  
  543. // this.PlayerContexts[ply].Vehicle = vehicle;
  544. // this.PlayerContexts[ply].VehicleInitialPosition = vehicle:GetPos();
  545.  
  546. // ply:EnterVehicle(vehicle);
  547. // }
  548.  
  549. // DEBUG: Makes one bot exit the race
  550. timer.Simple(6, function() {
  551. //this:CancelPlayer(this.Players[2]);
  552. });
  553.  
  554. this:BeginCountdown();
  555. }
  556.  
  557. function CreateDebugVehicle(position, angle) {
  558. return this:SpawnVehicle("Jeep", position, angle);
  559. }
  560.  
  561. // function PlayerLeaveVehicle(ply, context) {
  562. // RACING2.ChatMessage(ply, "You little shit, you got out of vehicle!!!");
  563. // this.SpawnedTrack:DestroyRace();
  564. // }
  565.  
  566. function PlayerCancelled(context) {
  567. if(context.Player:IsBot() && #this.Players > 1) {
  568. RACING2.ChatMessage(this.Players, "The race has been cancelled for " .. context.Player:Name());
  569. return;
  570. }
  571.  
  572. timer.Simple(0.5, function() {
  573. context.Player:SetPos(context.PlayerInitialPosition);
  574. });
  575.  
  576. this:CancelRace();
  577. }
  578.  
  579. function RaceEnded() {
  580. CRACING2_Race.RaceEnded(this);
  581.  
  582. foreach(var context in this.PlayerContexts) {
  583. timer.Simple(0.5, function() {
  584. context.Player:SetPos(context.PlayerInitialPosition);
  585. });
  586. }
  587.  
  588. RACING2.Demos.Save(this.Demo);
  589. }
  590. }
  591.  
  592. RACING2.Hook.Add("RACING2_RegisterRaceTypes", "register_race_debug", function() {
  593. RACING2.RegisterRaceConstructor("debug", RACING2_Race_Test);
  594. });
  595.  
  596. /*----------------------------
  597. Sprint
  598. ------------------------------*/
  599.  
  600. class RACING2_Race_Sprint : RACING2_Race {
  601. function this(spawnedTrack) {
  602. super(spawnedTrack);
  603. }
  604.  
  605. function Start() {
  606. super(this);
  607.  
  608. foreach(var i, ply in this.Players) {
  609. var vehicle = this:CreateDebugVehicle(this.Track:GetVehiclePosition(i),
  610. this.Track:GetDirectionFromPoint(1):Angle() + Angle(0, -90, 0));
  611.  
  612. this.PlayerContexts[ply].Vehicle = vehicle;
  613. this.PlayerContexts[ply].VehicleInitialPosition = vehicle:GetPos();
  614.  
  615. ply:EnterVehicle(vehicle);
  616. }
  617.  
  618. this:BeginCountdown();
  619. }
  620.  
  621. function CreateDebugVehicle(position, angle) {
  622. return this:SpawnVehicle("Jeep", position, angle);
  623. }
  624.  
  625. // function PlayerLeaveVehicle(ply, context) {
  626. // RACING2.ChatMessage(ply, "You little shit, you got out of vehicle!!!");
  627. // this.SpawnedTrack:DestroyRace();
  628. // }
  629.  
  630. function PlayerCancelled(context) {
  631. this:CancelRace();
  632. }
  633. }
  634.  
  635. RACING2.Hook.Add("RACING2_RegisterRaceTypes", "register_race_sprint", function() {
  636. RACING2.RegisterRaceConstructor("sprint", RACING2_Race_Sprint);
  637. });
  638.  
  639. static if(DEBUG) {
  640. hook.Call("RACING2_RegisterRaceTypes");
  641. }
Advertisement
Add Comment
Please, Sign In to add comment