Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- RACING2.RaceStates = {
- Waiting = 0,
- Countdown = 1,
- Racing = 2,
- Finished = 3,
- };
- var MAX_LEAVE_COUNT = 1;
- /*
- Only data or functions?
- */
- class RACING2_RacePlayerContext {
- var Player;
- var VehicleProvider;
- var VehicleId;
- var PlayerInitialPosition;
- var Vehicle;
- var OwnVehicle = false; /// True if the vehicle belongs to the player.
- var VehicleInitialPosition;
- var PointIndex = 1;
- var Distance = 0;
- var Time;
- var LeftVehicleCount = 0;
- var Cleared = false;
- function this(ply, vehicleProvider, vehicleId) {
- this.VehicleProvider = vehicleProvider;
- this.VehicleId = vehicleId;
- this.Player = ply;
- }
- }
- /**
- Base class for all races. Each race type has its own class.
- */
- class RACING2_Race {
- /// String identifier of the race. Used in timers.
- var Id;
- var SpawnedTrack;
- var Track;
- var Players = { };
- var CancelledPlayers = { };
- /// Player is the key.
- var PlayerContexts = { };
- var CancelledContexts = { };
- var ContextByVehicle = { };
- var State = RACING2.RaceStates.Waiting;
- /// When the countdown ends
- var NextStart;
- /// CurTime() when the race starts (after the countdown).
- var StartTime;
- var Demo;
- function this(spawnedTrack) {
- this.SpawnedTrack = spawnedTrack;
- this.Track = spawnedTrack.Track;
- this.Id = "" .. this.Track.Id .. "_";
- this.Track:CachePoints();
- this.Demo = RACING2_Demo(this.Track);
- }
- function AddPlayer(ply, vehicleProvider, vehicleId) {
- assert(IsValid(ply), "Player is invalid.");
- this.Players[#this.Players + 1] = ply;
- var context = RACING2_RacePlayerContext(ply, vehicleProvider, vehicleId);
- context.IsBot = ply:IsBot();
- this.PlayerContexts[ply] = context;
- }
- function Start() {
- foreach(var context in this.PlayerContexts) {
- context.PlayerInitialPosition = context.Player:GetPos();
- }
- this:CreateTimers();
- this:CreateHooks();
- this.NextStart = CurTime() + RACING2Config.CountdownSeconds;
- RACING2.Networking.Race_Begin(this);
- this.Demo:TrackEvent("start");
- }
- /**
- Spawns a vehicle for the player base on the provider,
- sets its position and assigns it to the context.
- */
- function SpawnVehicleForPlayer(ply, index) {
- // TODO: Cancel the race if vehicle can't be spawned
- var provider = this.PlayerContexts[ply].VehicleProvider;
- var initialPosition = this.Track:GetVehiclePosition(index);
- var initialAngles = this.Track:GetDirectionFromPoint(1):Angle() + Angle(0, -90, 0);
- // No provider - use predefined provider
- if(!provider || provider == "")
- provider = RACING2.VehicleProviders["predefined"];
- var vehicle = provider:GetVehicleEntity(this.Track, this.PlayerContexts[ply].VehicleId, ply);
- if(!IsValid(vehicle)) {
- this:CancelRace();
- return;
- }
- vehicle:SetPos(initialPosition);
- vehicle:SetAngles(initialAngles);
- this.PlayerContexts[ply].Vehicle = vehicle;
- this.PlayerContexts[ply].VehicleInitialPosition = initialPosition;
- return vehicle;
- }
- function Destroy() {
- this.SpawnedTrack.Race = null;
- this:RemoveTimers();
- this:RemoveHooks();
- foreach(var context in this.PlayerContexts)
- this:ClearPlayer(context.Player);
- foreach(var context in this.CancelledContexts)
- this:ClearPlayer(context.Player);
- RACING2.Networking.Race_End(this);
- }
- /*----------------------------
- Timers
- ------------------------------*/
- function CreateTimers() {
- RACING2.Timer.Create(this.Id .. "recordDemo", 0.1, 0, function() {
- foreach(var context in this.PlayerContexts)
- this.Demo:TrackVehicle(context.Player, context.Vehicle);
- });
- }
- function RemoveTimers() {
- RACING2.Timer.Remove(this.Id .. "updateRacing");
- RACING2.Timer.Remove(this.Id .. "wonRace");
- RACING2.Timer.Remove(this.Id .. "countdown");
- RACING2.Timer.Remove(this.Id .. "recordDemo");
- }
- /*----------------------------
- Hooks
- ------------------------------*/
- function CreateHooks() {
- RACING2.Hook.Add("PlayerLeaveVehicle", this.Id .. "leaveVehicle", function(ply, vehicle) {
- var context = this.PlayerContexts[ply];
- if(context && context.Vehicle == vehicle)
- this:PlayerLeaveVehicle(ply, context);
- });
- RACING2.Hook.Add("PlayerDisconnected", this.Id .. "disconnect", function(ply) {
- var context = this.PlayerContexts[ply];
- if(context)
- this:PlayerDisconnected(ply, context);
- });
- // For recording demo sounds
- RACING2.Hook.Add("EntityEmitSound", this.Id .. "emitSound", function(tbl) {
- var context = this.ContextByVehicle[tbl.Entity];
- if(context)
- this.Demo:TrackSound(context.Player, tbl);
- });
- }
- function RemoveHooks() {
- RACING2.Hook.Remove("PlayerLeaveVehicle", this.Id .. "leaveVehicle");
- RACING2.Hook.Remove("PlayerDisconnected", this.Id .. "disconnect");
- RACING2.Hook.Remove("Think", this.Id .. "freezeVehicles");
- RACING2.Hook.Remove("EntityEmitSound", this.Id .. "emitSound");
- }
- function SpawnVehicle(name, position, angle) {
- var tbl = list.Get("Vehicles")[name];
- var vehicle = ents.Create(tbl.Class);
- vehicle:SetPos(position);
- vehicle:SetAngles(angle);
- vehicle:SetModel(tbl.Model);
- foreach(var key, value in tbl.KeyValues)
- vehicle:SetKeyValue(key, value);
- vehicle:Spawn();
- return vehicle;
- }
- /*----------------------------
- Countdown state
- ------------------------------*/
- /**
- Isn't called automatically by Start.
- Players are in their vehicles now.
- */
- function BeginCountdown() {
- foreach(var context in this.PlayerContexts) {
- this.ContextByVehicle[context.Vehicle] = context;
- this.Demo:AddPlayer(context.Player, context.Vehicle);
- }
- this.State = RACING2.RaceStates.Countdown;
- this:CreateVehicleFreezeHook();
- var countdownSeconds = RACING2Config.CountdownSeconds;
- RACING2.Timer.Create(this.Id .. "countdown", 1, countdownSeconds, function() {
- countdownSeconds--;
- foreach(var context in this.PlayerContexts)
- RACING2.ChatMessage(context.Player, "" .. countdownSeconds);
- if(countdownSeconds == 0) {
- this:RemoveVehicleFreezeHook();
- this:BeginRacing();
- }
- });
- }
- function CreateVehicleFreezeHook() {
- RACING2.Hook.Add("Think", this.Id .. "freezeVehicles", function() {
- foreach(var context in this.PlayerContexts) {
- if(IsValid(context.Vehicle))
- context.Vehicle:SetPos(context.VehicleInitialPosition);
- }
- });
- }
- function RemoveVehicleFreezeHook() {
- RACING2.Hook.Remove("Think", this.Id .. "freezeVehicles");
- }
- /*----------------------------
- Racing state
- ------------------------------*/
- /**
- Called automatically by countdown timer.
- */
- function BeginRacing() {
- this.State = RACING2.RaceStates.Racing;
- this.StartTime = CurTime();
- // UpdateRacing timer
- RACING2.Timer.Create(this.Id .. "updateRacing", 0.1, 0, function() { this:UpdateRacing(); });
- RACING2.Networking.Race_Racing(this);
- this.Demo:TrackEvent("racing");
- }
- function UpdateRacing() {
- if(this.State != RACING2.RaceStates.Racing)
- return;
- foreach(var context in this.PlayerContexts) {
- if(context.PointIndex == #this.Track.CachedPoints)
- continue;
- this:UpdateDistance(context);
- this:UpdatePoints(context);
- if(context.IsBot)
- this:UpdateBot(context);
- }
- }
- function UpdatePoints(context) {
- if(context.PointIndex >= #this.Track.CachedPoints)
- return;
- var curPoint = this.Track.CachedPoints[context.PointIndex];
- var nextPoint = this.Track.CachedPoints[context.PointIndex + 1];
- var direction = this.Track:GetDirectionFromCachedPoint(context.PointIndex);
- var directionFromNextToPlayer = (context.Vehicle:GetPos() - nextPoint);
- var dotProduct = direction:Dot(directionFromNextToPlayer);
- // We've reached the point, update the index
- if(dotProduct > 0) {
- context.PointIndex = context.PointIndex + 1;
- if(context.PointIndex == #this.Track.CachedPoints)
- this:ReachedLastPoint(context);
- //else
- // RACING2.ChatMessage(context.Player, "Damn! Go to the next one!");
- RACING2.Networking.Race_Point(context.Player, context.PointIndex);
- this:UpdateScoreboard();
- }
- }
- function UpdateDistance(context) {
- var distance = 0;
- for(var i = 1; i <= context.PointIndex; i++) {
- var point = this.Track.CachedPoints[i];
- var nextPoint = this.Track.CachedPoints[i + 1];
- distance += (point - nextPoint):Length();
- }
- if(context.PointIndex != #this.Track.CachedPoints)
- distance -= (context.Vehicle:GetPos() - this.Track.CachedPoints[context.PointIndex + 1]):Length();
- context.Distance = distance;
- }
- var LastScoreboard = { };
- function UpdateScoreboard() {
- var scoreboard = { };
- foreach(var context in this.PlayerContexts)
- scoreboard[#scoreboard + 1] = context.Player;
- table.sort(scoreboard, function(a, b) {
- return this.PlayerContexts[a].PointIndex > this.PlayerContexts[b].PointIndex;
- });
- var shouldUpdate = false;
- foreach(var k, v in scoreboard) {
- if(this.LastScoreboard[k] != v) {
- shouldUpdate = true;
- break;
- }
- }
- if(shouldUpdate) {
- RACING2.Networking.Race_Scoreboard(this, scoreboard);
- this.LastScoreboard = scoreboard;
- }
- }
- // TODO: Multiple laps
- function ReachedLastPoint(context) {
- this:PlayerWon(context);
- }
- function PlayerWon(context) {
- context.Time = CurTime() - this.StartTime;
- this.WinnerPlayer = context.Player;
- this:BeginFinished();
- }
- function UpdateBot(context) {
- var ply = context.Player;
- var vehicle = context.Vehicle;
- var po = vehicle:GetPhysicsObject();
- po:ApplyForceCenter(vehicle:GetForward() * po:GetMass() * 40);
- var curPoint = this.Track.CachedPoints[context.PointIndex];
- var nextPoint = this.Track.CachedPoints[context.PointIndex + 1];
- if(!nextPoint) {
- curPoint = this.Track.CachedPoints[context.PointIndex - 1];
- nextPoint = this.Track.CachedPoints[context.PointIndex];
- }
- var diff = (nextPoint - curPoint);
- //var ang = diff:Angle().y - vehicle:GetAngles().y;
- var dAng = (diff):Angle();
- dAng.p = 0;
- dAng.r = 0;
- dAng.y = dAng.y - 90;
- var ang = vehicle:WorldToLocalAngles(dAng);
- po:AddAngleVelocity(Vector(0, 0, ang.y * 15));
- //vehicle:SetAngles(dAng);
- }
- function CancelPlayer(ply) {
- assert(this.PlayerContexts[ply], "That player has already been cancelled or didn't take part in the race.");
- var context = this.PlayerContexts[ply];
- // Must be here, because this.Players is used.
- RACING2.Networking.Race_PlayerCancelled(this, ply);
- this.CancelledContexts[ply] = this.PlayerContexts[ply];
- table.RemoveByValue(this.Players, ply);
- this.PlayerContexts[ply] = null;
- this.CancelledPlayers[#this.CancelledPlayers + 1] = ply;
- RACING2.ChatMessage(ply, RACING2.Language["The race has been cancelled for you."]);
- this:ClearPlayer(context.Player);
- this:PlayerCancelled(this.CancelledContexts[ply]);
- }
- function PlayerCancelled(context) {
- if(this.State == RACING2.RaceStates.Countdown) {
- RACING2.ChatMessage(this.Players, RACING2.Language["Someone left, cancelling the race."]);
- this:CancelRace();
- return;
- }
- if(#this.Players == 0)
- this:CancelRace();
- }
- function CancelRace() {
- this:RaceCancelled();
- this:RaceEnded();
- this:Destroy();
- }
- function RaceCancelled() {
- RACING2.ChatMessage(player.GetAll()[1], "The race has been cancelled.");
- }
- function ClearPlayer(ply) {
- var context = this.PlayerContexts[ply] ?? this.CancelledContexts[ply];
- assert(context, "No such player in the race.");
- if(context.Cleared)
- return;
- context.Cleared = true;
- if(!context.OwnVehicle && IsValid(context.Vehicle))
- context.Vehicle:Remove();
- }
- /**
- Called when the player taking a part in the race left their vehicle.
- Makes them enter the vehicle again a few times, to prevent cancelling
- the race by accident.
- */
- function PlayerLeaveVehicle(ply, context) {
- context.LeftVehicleCount = context.LeftVehicleCount + 1;
- if(context.LeftVehicleCount >= MAX_LEAVE_COUNT) {
- this:CancelPlayer(ply);
- return;
- }
- 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));
- var hookId = this.Track.Id .. "_ForceEnterVehicle" .. ply:EntIndex();
- RACING2.Hook.Add("Think", hookId, function() {
- if(ply:GetVehicle() == context.Vehicle || !IsValid(ply)) {
- RACING2.Hook.Remove("Think", hookId);
- return;
- }
- ply:EnterVehicle(context.Vehicle);
- });
- }
- function PlayerDisconnected(ply, context) {
- this:CancelPlayer(ply);
- }
- /*----------------------------
- Finished state
- ------------------------------*/
- function BeginFinished() {
- this.State = RACING2.RaceStates.Finished;
- RACING2.Timer.Remove(this.Id .. "updateRacing");
- foreach(var c in this.PlayerContexts) {
- RACING2.ChatMessage(c.Player, string.format(RACING2.Language["%s has won the race!"], this.WinnerPlayer:Name()));
- }
- var winnerContext = this.PlayerContexts[this.WinnerPlayer];
- RACING2.ChatMessage(this.WinnerPlayer, "You damn won! GG");
- RACING2.ChatMessage(this.WinnerPlayer, "Time: " .. winnerContext.Time);
- RACING2.Networking.Race_PlayerFinished(this.WinnerPlayer, winnerContext.Time);
- RACING2.Timer.Create(this.Id .. "wonRace", 5, 1, function() {
- this:RaceEnded();
- this:Destroy();
- });
- }
- // Called both when race finishes and is being cancelled.
- function RaceEnded() {
- this.Demo:TrackEvent("end");
- }
- }
- RACING2.Hook.Add("RACING2_RegisterRaceTypes", "register_race_race", function() {
- RACING2.RegisterRaceConstructor("race", RACING2_Race);
- });
- /*----------------------------
- Debug race
- ------------------------------*/
- class RACING2_Race_Test : RACING2_Race {
- function this(spawnedTrack) {
- super(spawnedTrack);
- }
- function Start() {
- super();
- foreach(var i, ply in this.Players) {
- var vehicle = this:SpawnVehicleForPlayer(ply, i);
- ply:EnterVehicle(vehicle);
- }
- // foreach(var i, ply in this.Players) {
- // // TODO: Later move this to the base class
- // var vehicle = this:CreateDebugVehicle(this.Track:GetVehiclePosition(i),
- // this.Track:GetDirectionFromPoint(1):Angle() + Angle(0, -90, 0));
- // this.PlayerContexts[ply].Vehicle = vehicle;
- // this.PlayerContexts[ply].VehicleInitialPosition = vehicle:GetPos();
- // ply:EnterVehicle(vehicle);
- // }
- // DEBUG: Makes one bot exit the race
- timer.Simple(6, function() {
- //this:CancelPlayer(this.Players[2]);
- });
- this:BeginCountdown();
- }
- function CreateDebugVehicle(position, angle) {
- return this:SpawnVehicle("Jeep", position, angle);
- }
- // function PlayerLeaveVehicle(ply, context) {
- // RACING2.ChatMessage(ply, "You little shit, you got out of vehicle!!!");
- // this.SpawnedTrack:DestroyRace();
- // }
- function PlayerCancelled(context) {
- if(context.Player:IsBot() && #this.Players > 1) {
- RACING2.ChatMessage(this.Players, "The race has been cancelled for " .. context.Player:Name());
- return;
- }
- timer.Simple(0.5, function() {
- context.Player:SetPos(context.PlayerInitialPosition);
- });
- this:CancelRace();
- }
- function RaceEnded() {
- CRACING2_Race.RaceEnded(this);
- foreach(var context in this.PlayerContexts) {
- timer.Simple(0.5, function() {
- context.Player:SetPos(context.PlayerInitialPosition);
- });
- }
- RACING2.Demos.Save(this.Demo);
- }
- }
- RACING2.Hook.Add("RACING2_RegisterRaceTypes", "register_race_debug", function() {
- RACING2.RegisterRaceConstructor("debug", RACING2_Race_Test);
- });
- /*----------------------------
- Sprint
- ------------------------------*/
- class RACING2_Race_Sprint : RACING2_Race {
- function this(spawnedTrack) {
- super(spawnedTrack);
- }
- function Start() {
- super(this);
- foreach(var i, ply in this.Players) {
- var vehicle = this:CreateDebugVehicle(this.Track:GetVehiclePosition(i),
- this.Track:GetDirectionFromPoint(1):Angle() + Angle(0, -90, 0));
- this.PlayerContexts[ply].Vehicle = vehicle;
- this.PlayerContexts[ply].VehicleInitialPosition = vehicle:GetPos();
- ply:EnterVehicle(vehicle);
- }
- this:BeginCountdown();
- }
- function CreateDebugVehicle(position, angle) {
- return this:SpawnVehicle("Jeep", position, angle);
- }
- // function PlayerLeaveVehicle(ply, context) {
- // RACING2.ChatMessage(ply, "You little shit, you got out of vehicle!!!");
- // this.SpawnedTrack:DestroyRace();
- // }
- function PlayerCancelled(context) {
- this:CancelRace();
- }
- }
- RACING2.Hook.Add("RACING2_RegisterRaceTypes", "register_race_sprint", function() {
- RACING2.RegisterRaceConstructor("sprint", RACING2_Race_Sprint);
- });
- static if(DEBUG) {
- hook.Call("RACING2_RegisterRaceTypes");
- }
Advertisement
Add Comment
Please, Sign In to add comment