competitive mode and small bugfixes

This commit is contained in:
RandomityGuy 2024-07-07 19:33:16 +05:30
parent 26ed1cb820
commit f9a58d15a9
13 changed files with 363 additions and 60 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1861,19 +1861,21 @@ class Marble extends GameObject {
this.updateRollSound(timeState, contactTime / timeState.dt, this._slipAmount);
var megaMarbleDurationTicks = Net.connectedServerInfo.competitiveMode ? 156 : 312;
if (this.megaMarbleUseTick > 0) {
if (Net.isHost) {
if ((timeState.ticks - this.megaMarbleUseTick) <= 312 && this.megaMarbleUseTick > 0) {
if ((timeState.ticks - this.megaMarbleUseTick) <= megaMarbleDurationTicks && this.megaMarbleUseTick > 0) {
this._radius = 0.6666;
this.collider.radius = 0.6666;
} else if ((timeState.ticks - this.megaMarbleUseTick) > 312) {
} else if ((timeState.ticks - this.megaMarbleUseTick) > megaMarbleDurationTicks) {
this.collider.radius = this._radius = 0.2;
this.megaMarbleUseTick = 0;
this.netFlags |= MarbleNetFlags.DoMega;
}
}
if (Net.isClient) {
if (this.serverTicks - this.megaMarbleUseTick <= 312 && this.megaMarbleUseTick > 0) {
if (this.serverTicks - this.megaMarbleUseTick <= megaMarbleDurationTicks && this.megaMarbleUseTick > 0) {
this._radius = 0.6666;
this.collider.radius = 0.6666;
} else {
@ -1887,7 +1889,7 @@ class Marble extends GameObject {
}
if (Net.isMP) {
if (m.jump && this.outOfBounds) {
if (m.powerup && this.outOfBounds) {
this.level.cancel(this.oobSchedule);
this.level.restart(cast this);
}
@ -1896,7 +1898,7 @@ class Marble extends GameObject {
interior.popTickState();
}
if (m.respawn) {
if (m.respawn && !Net.connectedServerInfo.competitiveMode) { // Competitive mode disables quick respawning
if (timeState.ticks - lastRespawnTick > (25000 >> 5)) {
this.level.restart(cast this);
lastRespawnTick = timeState.ticks;
@ -2418,17 +2420,19 @@ class Marble extends GameObject {
new Vector(1, 1, 1).add(new Vector(Math.abs(this.currentUp.x), Math.abs(this.currentUp.y), Math.abs(this.currentUp.z)).multiply(-0.8)));
this.blastTicks = 0;
// Now send the impulse to other marbles
var strength = blastAmt * (blastAmt > 1 ? blastRechargeShockwaveStrength : blastShockwaveStrength);
var ourPos = this.collider.transform.getPosition();
for (marble in level.marbles) {
if (marble != cast this) {
var theirPos = marble.collider.transform.getPosition();
var posDiff = ourPos.distance(theirPos);
if (posDiff < strength) {
var myMod = isMegaMarbleEnabled(timeState) ? 0.7 : 1.0;
var theirMod = @:privateAccess marble.isMegaMarbleEnabled(timeState) ? 0.7 : 1.0;
var impulse = theirPos.sub(ourPos).normalized().multiply(strength * (theirMod / myMod));
marble.applyImpulse(impulse);
if (!Net.connectedServerInfo.competitiveMode || blastAmt > 1) { // Competitor mode only allows ultra blasts
var strength = blastAmt * (blastAmt > 1 ? blastRechargeShockwaveStrength : blastShockwaveStrength);
var ourPos = this.collider.transform.getPosition();
for (marble in level.marbles) {
if (marble != cast this) {
var theirPos = marble.collider.transform.getPosition();
var posDiff = ourPos.distance(theirPos);
if (posDiff < strength) {
var myMod = isMegaMarbleEnabled(timeState) ? 0.7 : 1.0;
var theirMod = @:privateAccess marble.isMegaMarbleEnabled(timeState) ? 0.7 : 1.0;
var impulse = theirPos.sub(ourPos).normalized().multiply(strength * (theirMod / myMod));
marble.applyImpulse(impulse);
}
}
}
}
@ -2527,15 +2531,16 @@ class Marble extends GameObject {
}
inline function isMegaMarbleEnabled(timeState:TimeState) {
var megaMarbleTicks = Net.connectedServerInfo.competitiveMode ? 156 : 312;
if (this.level == null)
return false;
if (!this.level.isMultiplayer) {
return timeState.currentAttemptTime - this.megaMarbleEnableTime < 10;
} else {
if (Net.isHost) {
return (megaMarbleUseTick > 0 && (this.level.timeState.ticks - megaMarbleUseTick) <= 312);
return (megaMarbleUseTick > 0 && (this.level.timeState.ticks - megaMarbleUseTick) <= megaMarbleTicks);
} else {
return (megaMarbleUseTick > 0 && (serverTicks - megaMarbleUseTick) <= 312);
return (megaMarbleUseTick > 0 && (serverTicks - megaMarbleUseTick) <= megaMarbleTicks);
}
}
}

View file

@ -386,6 +386,7 @@ class MarbleWorld extends Scheduler {
this.endPad.generateCollider();
if (this.isMultiplayer) {
this.playGui.formatGemHuntCounter(0);
this.playGui.formatCountdownTimer(0, 0);
} else {
this.playGui.formatGemCounter(this.gemCount, this.totalGems);
}
@ -1684,10 +1685,10 @@ class MarbleWorld extends Scheduler {
return -1;
}
public function spawnHuntGemsClientSide(gemIds:Array<Int>) {
public function spawnHuntGemsClientSide(gemIds:Array<Int>, expireds:Array<Bool>) {
if (this.isMultiplayer && Net.isClient) {
var huntMode:HuntMode = cast this.gameMode;
huntMode.setActiveSpawnSphere(gemIds);
huntMode.setActiveSpawnSphere(gemIds, expireds);
// radar.blink();
}
}
@ -1829,6 +1830,7 @@ class MarbleWorld extends Scheduler {
ProfilerUI.measure("updateTimer");
this.updateTimer(dt);
this.gameMode.update(this.timeState);
if (!this.isMultiplayer) {
if ((Key.isPressed(Settings.controlsSettings.respawn) || Gamepad.isPressed(Settings.gamepadSettings.respawn))
@ -2838,7 +2840,7 @@ class MarbleWorld extends Scheduler {
this.deselectPowerUp(this.marble); // Always deselect first
// Wait a bit to select the powerup to prevent immediately using it incase the user skipped the OOB screen by clicking
if (this.checkpointHeldPowerup != null)
this.pickUpPowerUp(this.marble, this.checkpointHeldPowerup);
this.schedule(this.timeState.currentAttemptTime + 0.5, () -> this.pickUpPowerUp(this.marble, this.checkpointHeldPowerup));
AudioManager.playSound(ResourceLoader.getResource('data/sound/spawn.wav', ResourceLoader.getAudio, this.soundResources));
}

View file

@ -244,11 +244,11 @@ class Mission {
return path;
if (ResourceLoader.exists(dirpath + fname))
return dirpath + fname;
if (game == 'gold') {
path = StringTools.replace(path, 'interiors/', 'interiors_mbg/');
if (ResourceLoader.exists(path))
return path;
}
path = StringTools.replace(path, 'interiors/', 'interiors_mbg/');
if (ResourceLoader.exists(path))
return path;
path = StringTools.replace(path, "lbinteriors", "interiors"); // This shit ew
if (ResourceLoader.exists(path))
return path;

View file

@ -106,6 +106,7 @@ typedef ServerSettings = {
var password:String;
var forceSpectators:Bool;
var quickRespawn:Bool;
var competitiveMode:Bool;
}
typedef PlayStatistics = {
@ -210,7 +211,8 @@ class Settings {
maxPlayers: 8,
description: "My cool server",
forceSpectators: false,
quickRespawn: true
quickRespawn: true,
competitiveMode: false,
}
public static var levelStatistics:Map<String, PlayStatistics> = [];

View file

@ -95,6 +95,7 @@ class MPServerDlg extends GuiImage {
var curServerMaxPlayers = Settings.serverSettings.maxPlayers;
var curServerForceSpectators = Settings.serverSettings.forceSpectators;
var curServerQuickRespawn = Settings.serverSettings.quickRespawn;
var curServerCompetitive = Settings.serverSettings.competitiveMode;
saveBtn.pressedAction = (e) -> {
Settings.serverSettings.name = curServerName;
@ -103,6 +104,7 @@ class MPServerDlg extends GuiImage {
Settings.serverSettings.maxPlayers = curServerMaxPlayers;
Settings.serverSettings.forceSpectators = curServerForceSpectators;
Settings.serverSettings.quickRespawn = curServerQuickRespawn;
Settings.serverSettings.competitiveMode = curServerCompetitive;
if (Net.isHost) {
Net.serverInfo.name = curServerName;
Net.serverInfo.description = curServerDescription;
@ -110,7 +112,7 @@ class MPServerDlg extends GuiImage {
Net.serverInfo.password = curServerPassword;
MasterServerClient.instance.sendServerInfo(Net.serverInfo); // Update data on master server
NetCommands.sendServerSettings(Settings.serverSettings.name, Settings.serverSettings.description, Settings.serverSettings.quickRespawn,
Settings.serverSettings.forceSpectators);
Settings.serverSettings.forceSpectators, Settings.serverSettings.competitiveMode);
}
MarbleGame.canvas.popDialog(this);
}
@ -119,7 +121,7 @@ class MPServerDlg extends GuiImage {
serverSettingsContainer.vertSizing = Height;
serverSettingsContainer.horizSizing = Left;
serverSettingsContainer.position = new Vector(16, 65);
serverSettingsContainer.extent = new Vector(390, 276);
serverSettingsContainer.extent = new Vector(390, 306);
this.addChild(serverSettingsContainer);
var serverName = new GuiText(markerFelt18);
@ -306,5 +308,28 @@ class MPServerDlg extends GuiImage {
curServerQuickRespawn = !curServerQuickRespawn;
};
serverSettingsContainer.addChild(quickRespawnChk);
var competitive = new GuiText(markerFelt18);
competitive.text.text = "Competitive Mode:";
competitive.text.textColor = 0xFFFFFF;
competitive.text.dropShadow = {
dx: 1,
dy: 1,
alpha: 0.5,
color: 0
};
competitive.position = new Vector(0, 39 * 7);
competitive.extent = new Vector(206, 14);
serverSettingsContainer.addChild(competitive);
var competitiveChk = new GuiButton(loadButtonImages("data/ui/mp/lb_chkbx"));
competitiveChk.position = new Vector(359, 9 * 4 + 29 * 8 + 4);
competitiveChk.extent = new Vector(31, 31);
competitiveChk.buttonType = Toggle;
competitiveChk.pressed = curServerCompetitive;
competitiveChk.pressedAction = (sender) -> {
curServerCompetitive = !curServerCompetitive;
};
serverSettingsContainer.addChild(competitiveChk);
}
}

View file

@ -62,6 +62,10 @@ class PlayGui {
var timerPoint:GuiAnim;
var timerColon:GuiAnim;
var countdownNumbers:Array<GuiAnim> = [];
var countdownPoint:GuiAnim;
var countdownIcon:GuiImage;
var gemCountNumbers:Array<GuiAnim> = [];
var gemCountSlash:GuiImage;
var gemImageScene:h3d.scene.Scene;
@ -182,8 +186,14 @@ class PlayGui {
timerNumbers.push(new GuiAnim(numberTiles));
}
for (i in 0...6) {
gemCountNumbers.push(new GuiAnim(numberTiles));
if (MarbleGame.instance.world.isMultiplayer) {
for (i in 0...3) {
countdownNumbers.push(new GuiAnim(numberTiles));
}
for (i in 0...6) {
gemCountNumbers.push(new GuiAnim(numberTiles));
}
}
var rsgo = [];
@ -211,6 +221,8 @@ class PlayGui {
initChatHud();
if (Net.hostSpectate || Net.clientSpectate)
initSpectatorMenu();
initGemCountdownTimer();
}
if (Util.isTouchDevice()) {
@ -294,6 +306,44 @@ class PlayGui {
playGuiCtrl.addChild(timerCtrl);
}
public function initGemCountdownTimer() {
var timerCtrl = new GuiControl();
timerCtrl.horizSizing = HorizSizing.Center;
timerCtrl.position = new Vector(215, 1);
timerCtrl.extent = new Vector(374, 58);
countdownNumbers[0].position = new Vector(33, 10);
countdownNumbers[0].extent = new Vector(28, 37);
countdownNumbers[1].position = new Vector(49, 10);
countdownNumbers[1].extent = new Vector(28, 37);
var pointCols = [
ResourceLoader.getResource('data/ui/game/numbers/point.png', ResourceLoader.getImage, this.imageResources).toTile(),
ResourceLoader.getResource('data/ui/game/numbers/point_green.png', ResourceLoader.getImage, this.imageResources).toTile(),
ResourceLoader.getResource('data/ui/game/numbers/point_red.png', ResourceLoader.getImage, this.imageResources).toTile()
];
countdownPoint = new GuiAnim(pointCols);
countdownPoint.position = new Vector(59, 10);
countdownPoint.extent = new Vector(28, 37);
countdownNumbers[2].position = new Vector(70, 10);
countdownNumbers[2].extent = new Vector(28, 37);
countdownIcon = new GuiImage(ResourceLoader.getResource("data/ui/game/timerhuntrespawn.png", ResourceLoader.getImage, this.imageResources).toTile());
countdownIcon.position = new Vector(0, 10);
countdownIcon.extent = new Vector(36, 36);
timerCtrl.addChild(countdownIcon);
timerCtrl.addChild(countdownNumbers[0]);
timerCtrl.addChild(countdownNumbers[1]);
timerCtrl.addChild(countdownPoint);
timerCtrl.addChild(countdownNumbers[2]);
playGuiCtrl.addChild(timerCtrl);
}
public function initCenterText() {
RSGOCenterText.x = scene2d.width / 2 - RSGOCenterText.frames[0].width * Settings.uiScale / 2;
RSGOCenterText.y = scene2d.height * 0.3; // - RSGOCenterText.frames[0].height / 2;
@ -988,6 +1038,44 @@ class PlayGui {
timerColon.anim.currentFrame = color;
}
public function formatCountdownTimer(time:Float, color:Int = 0) {
if (time == 0) {
countdownNumbers[0].anim.visible = false;
countdownNumbers[1].anim.visible = false;
countdownNumbers[2].anim.visible = false;
countdownPoint.anim.visible = false;
countdownIcon.bmp.visible = false;
} else {
countdownNumbers[0].anim.visible = true;
countdownNumbers[1].anim.visible = true;
countdownNumbers[2].anim.visible = true;
countdownPoint.anim.visible = true;
countdownIcon.bmp.visible = true;
}
var et = time * 1000;
var hundredth = Math.floor((et % 1000) / 10);
var totalSeconds = Math.floor(et / 1000);
var seconds = totalSeconds % 60;
var secondsOne = seconds % 10;
var secondsTen = (seconds - secondsOne) / 10;
var hundredthOne = hundredth % 10;
var hundredthTen = (hundredth - hundredthOne) / 10;
if (secondsTen > 0) {
countdownNumbers[0].anim.visible = true;
countdownNumbers[0].anim.currentFrame = secondsTen + color * 10;
} else {
countdownNumbers[0].anim.visible = false;
}
countdownNumbers[1].anim.currentFrame = secondsOne + color * 10;
countdownNumbers[2].anim.currentFrame = hundredthTen + color * 10;
countdownPoint.anim.currentFrame = color;
}
public function render(engine:h3d.Engine) {
engine.pushTarget(this.gemImageSceneTarget);

View file

@ -1,5 +1,6 @@
package modes;
import src.TimeState;
import src.Marble;
import shapes.Gem;
import h3d.Quat;
@ -27,6 +28,7 @@ interface GameMode {
public function onClientRestart():Void;
public function onRespawn(marble:Marble):Void;
public function onGemPickup(marble:Marble, gem:Gem):Void;
public function update(t:TimeState):Void;
public function getPreloadFiles():Array<String>;
}

View file

@ -70,8 +70,13 @@ class HuntMode extends NullMode {
var activeGemSpawnGroup:Array<Int>;
var gemBeams:Array<GemBeam> = [];
var gemToBeamMap:Map<Gem, GemBeam> = [];
var gemToBlackBeamMap:Map<Gem, GemBeam> = [];
var activeGems:Array<Gem> = [];
var points:Int = 0;
var gemsCentroid:Vector;
var idealSpawnIndex:Int;
var expiredGems:Map<Gem, Bool> = [];
var competitiveTimerStartTicks:Int;
override function missionScan(mission:Mission) {
function scanMission(simGroup:MissionElementSimGroup) {
@ -92,11 +97,13 @@ class HuntMode extends NullMode {
};
override function getSpawnTransform() {
var idx = Math.floor(rng2.randRange(0, playerSpawnPoints.length - 1));
while (spawnPointTaken[idx]) {
idx = Math.floor(rng2.randRange(0, playerSpawnPoints.length - 1));
var idx = Net.connectedServerInfo.competitiveMode ? idealSpawnIndex : Math.floor(rng2.randRange(0, playerSpawnPoints.length - 1));
if (!Net.connectedServerInfo.competitiveMode) {
while (spawnPointTaken[idx]) {
idx = Math.floor(rng2.randRange(0, playerSpawnPoints.length - 1));
}
spawnPointTaken[idx] = true;
}
spawnPointTaken[idx] = true;
var randomSpawn = playerSpawnPoints[idx];
var spawnPos = MisParser.parseVector3(randomSpawn.position);
@ -174,6 +181,7 @@ class HuntMode extends NullMode {
if (this.gemSpawnPoints == null) {
this.gemOctree = new Octree();
this.gemSpawnPoints = [];
this.gemsCentroid = new Vector();
for (gem in this.level.gems) {
var spawn:GemSpawnPoint = new GemSpawnPoint(gem.getAbsPos().getPosition(), gem, gemSpawnPoints.length);
gem.setHide(true);
@ -185,6 +193,21 @@ class HuntMode extends NullMode {
if (level.isMultiplayer) {
@:privateAccess level.gemPredictions.alloc();
}
gemsCentroid.load(gemsCentroid.add(gem.getAbsPos().getPosition()));
}
if (gemSpawnPoints.length > 0)
gemsCentroid.load(gemsCentroid.multiply(1.0 / gemSpawnPoints.length));
var closestSpawnIndex = 0;
var closestSpawnDistance = 1e8;
for (i in 0...playerSpawnPoints.length) {
var spawn = playerSpawnPoints[i];
var spawnPos = MisParser.parseVector3(spawn.position);
spawnPos.x *= -1;
if (spawnPos.distance(gemsCentroid) < closestSpawnDistance) {
closestSpawnDistance = spawnPos.distance(gemsCentroid);
closestSpawnIndex = i;
}
}
}
for (i in 0...spawnPointTaken.length) {
@ -206,8 +229,8 @@ class HuntMode extends NullMode {
spawnHuntGems();
}
function spawnHuntGems() {
if (activeGems.length != 0)
function spawnHuntGems(force:Bool = false) {
if (activeGems.length != 0 && !force)
return;
var gemGroupRadius = 15.0;
var maxGemsPerSpawn = 7;
@ -267,16 +290,39 @@ class HuntMode extends NullMode {
return 0;
});
var spawnSet = results.slice(0, maxGemsPerSpawn).map(x -> x.gem);
if (force) {
for (activeGem in activeGemSpawnGroup)
spawnSet.remove(activeGem);
}
for (gem in spawnSet) {
spawnGem(gem);
}
activeGemSpawnGroup = spawnSet;
if (!force)
activeGemSpawnGroup = spawnSet;
else {
var uncollectedGems = [];
for (g in activeGemSpawnGroup) {
if (!gemSpawnPoints[g].gem.pickedUp)
uncollectedGems.push(g);
}
activeGemSpawnGroup = uncollectedGems.concat(spawnSet);
}
if (level.isMultiplayer && Net.isHost) {
var bs = new OutputBitStream();
bs.writeByte(GemSpawn);
var packet = new GemSpawnPacket();
packet.gemIds = spawnSet;
packet.gemIds = activeGemSpawnGroup;
packet.expireds = [];
for (i in 0...packet.gemIds.length) {
if (expiredGems.exists(gemSpawnPoints[packet.gemIds[i]].gem)) {
packet.expireds.push(true);
} else {
packet.expireds.push(false);
}
}
packet.serialize(bs);
Net.sendPacketToIngame(bs);
}
@ -284,29 +330,43 @@ class HuntMode extends NullMode {
lastSpawn = furthest;
}
function spawnGem(spawn:Int) {
function spawnGem(spawn:Int, expired:Bool = false) {
var gem = gemSpawnPoints[spawn];
gem.gem.setHide(false);
gem.gem.pickedUp = false;
this.level.collisionWorld.addEntity(gem.gem.boundingCollider);
activeGems.push(gem.gem);
if (gem.gemBeam == null) {
gem.gemBeam = new GemBeam(StringTools.replace(gem.gem.gemColor, '.gem', ''));
if (!expired) {
if (gem.gemBeam == null) {
gem.gemBeam = new GemBeam(StringTools.replace(gem.gem.gemColor, '.gem', ''));
var gemPos = gem.gem.getAbsPos().getPosition();
var gemPos = gem.gem.getAbsPos().getPosition();
gem.gemBeam.setPosition(gemPos.x, gemPos.y, gemPos.z);
gem.gemBeam.setRotationQuat(gem.gem.getRotationQuat().clone());
// gem.gemBeam.setOpacity(0.99);
this.gemBeams.push(gem.gemBeam);
gem.gemBeam.setPosition(gemPos.x, gemPos.y, gemPos.z);
gem.gemBeam.setRotationQuat(gem.gem.getRotationQuat().clone());
// gem.gemBeam.setOpacity(0.99);
this.gemBeams.push(gem.gemBeam);
this.gemToBeamMap.set(gem.gem, gem.gemBeam);
this.gemToBeamMap.set(gem.gem, gem.gemBeam);
level.addDtsObject(gem.gemBeam, () -> {
// Please be fast lol
});
level.addDtsObject(gem.gemBeam, () -> {
// Please be fast lol
});
} else {
gem.gemBeam.setHide(false);
}
} else {
gem.gemBeam.setHide(false);
if (gemToBlackBeamMap.exists(gem.gem)) {
gemToBlackBeamMap.get(gem.gem).setHide(false);
} else {
var blackBeam = new GemBeam("black");
var pos = gem.gem.getAbsPos().getPosition();
blackBeam.setPosition(gem.gem.x, gem.gem.y, gem.gem.z);
blackBeam.setRotationQuat(gem.gem.getRotationQuat().clone());
blackBeam.setHide(false);
level.addDtsObject(blackBeam, () -> {});
gemToBlackBeamMap.set(gem.gem, blackBeam);
}
}
}
@ -325,10 +385,11 @@ class HuntMode extends NullMode {
}
}
public function setActiveSpawnSphere(gems:Array<Int>) {
public function setActiveSpawnSphere(gems:Array<Int>, expireds:Array<Bool>) {
hideExisting();
for (gem in gems) {
spawnGem(gem);
for (i in 0...gems.length) {
var gem = gems[i];
spawnGem(gem, expireds[i]);
}
}
@ -351,6 +412,9 @@ class HuntMode extends NullMode {
if (gs.gemBeam != null) {
gs.gemBeam.setHide(true);
}
if (gemToBlackBeamMap.exists(gs.gem)) {
gemToBlackBeamMap.get(gs.gem).setHide(true);
}
}
}
}
@ -362,15 +426,18 @@ class HuntMode extends NullMode {
override function onRestart() {
setupGems();
points = 0;
competitiveTimerStartTicks = 0;
@:privateAccess level.playGui.formatGemHuntCounter(points);
}
override function onMissionLoad() {
prepareGems();
competitiveTimerStartTicks = 0;
}
override function onClientRestart() {
prepareGems();
competitiveTimerStartTicks = 0;
}
override function onTimeExpire() {
@ -420,6 +487,17 @@ class HuntMode extends NullMode {
@:privateAccess this.level.soundResources));
}
activeGems.remove(gem);
var wasExpiredGem = false;
if (expiredGems.exists(gem)) {
wasExpiredGem = true;
expiredGems.remove(gem);
}
if (gemToBlackBeamMap.exists(gem)) {
gemToBlackBeamMap.get(gem).setHide(true);
}
var beam = gemToBeamMap.get(gem);
beam.setHide(true);
@ -455,6 +533,43 @@ class HuntMode extends NullMode {
}
if (this.level.isMultiplayer && Net.isHost) {
if (Net.connectedServerInfo.competitiveMode && !wasExpiredGem) {
if (competitiveTimerStartTicks == 0) {
NetCommands.setCompetitiveTimerStartTicks(this.level.timeState.ticks);
}
var remaining = 0;
for (g in activeGems)
if (!expiredGems.exists(g))
remaining++;
if (remaining == 3) {
var currentTime = level.timeState.ticks;
var endTime = competitiveTimerStartTicks + (20000 >> 5);
var remainingTicks = (endTime - currentTime);
if (remainingTicks > (15000 >> 5)) {
NetCommands.setCompetitiveTimerStartTicks(currentTime - (5000 >> 5));
}
}
if (remaining == 2) {
var currentTime = level.timeState.ticks;
var endTime = competitiveTimerStartTicks + (20000 >> 5);
var remainingTicks = (endTime - currentTime);
if (remainingTicks > (10000 >> 5)) {
NetCommands.setCompetitiveTimerStartTicks(currentTime - (10000 >> 5));
}
}
if (remaining == 1) {
var currentTime = level.timeState.ticks;
var endTime = competitiveTimerStartTicks + (20000 >> 5);
var remainingTicks = (endTime - currentTime);
if (remainingTicks > (5000 >> 5)) {
NetCommands.setCompetitiveTimerStartTicks(currentTime - (15000 >> 5));
}
}
if (remaining == 0) {
NetCommands.setCompetitiveTimerStartTicks(0);
}
}
var packet = new GemPickupPacket();
packet.clientId = @:privateAccess marble.connection == null ? 0 : @:privateAccess marble.connection.id;
packet.gemId = gem.netIndex;
@ -474,6 +589,47 @@ class HuntMode extends NullMode {
}
}
public function setCompetitiveTimerStartTicks(ticks:Int) {
competitiveTimerStartTicks = ticks;
}
function spawnNextGemCluster() {
// Expire all existing
for (g in activeGems) {
expiredGems.set(g, true);
var gemBeam = gemToBeamMap.get(g);
gemBeam.setHide(true);
if (gemToBlackBeamMap.exists(g)) {
gemToBlackBeamMap.get(g).setHide(false);
} else {
var blackBeam = new GemBeam("black");
var pos = g.getAbsPos().getPosition();
blackBeam.setPosition(g.x, g.y, g.z);
blackBeam.setRotationQuat(g.getRotationQuat().clone());
blackBeam.setHide(false);
level.addDtsObject(blackBeam, () -> {});
gemToBlackBeamMap.set(g, blackBeam);
}
}
spawnHuntGems(true);
}
override function update(t:src.TimeState) {
if (Net.connectedServerInfo.competitiveMode) {
if (competitiveTimerStartTicks != 0) {
var currentTime = Net.isHost ? t.ticks : @:privateAccess level.marble.serverTicks;
var endTime = competitiveTimerStartTicks + (20000 >> 5);
@:privateAccess level.playGui.formatCountdownTimer(Math.max(0, (endTime - currentTime) * 0.032), 0);
if (Net.isHost && endTime < currentTime) {
spawnNextGemCluster();
NetCommands.setCompetitiveTimerStartTicks(0);
}
} else {
@:privateAccess level.playGui.formatCountdownTimer(0, 0);
}
}
}
override public function timeMultiplier() {
return -1;
}

View file

@ -138,4 +138,6 @@ class NullMode implements GameMode {
public function onClientRestart() {}
public function onMissionLoad() {}
public function update(t:src.TimeState) {}
}

View file

@ -73,6 +73,7 @@ class ConnectedServerInfo {
var description:String;
var quickRespawn:Bool;
var forceSpectator:Bool;
var competitiveMode:Bool;
}
class Net {
@ -112,6 +113,13 @@ class Net {
clientId = 0;
isMP = true;
MasterServerClient.instance.sendServerInfo(serverInfo);
Net.connectedServerInfo = {
name: name,
description: description,
competitiveMode: Settings.serverSettings.competitiveMode,
quickRespawn: Settings.serverSettings.quickRespawn,
forceSpectator: Settings.serverSettings.forceSpectators,
};
onHosted();
});
}
@ -602,7 +610,7 @@ class Net {
// NetCommands.setLobbyCustLevelNameClient(conn, MultiplayerLevelSelectGui.custPath);
// } else {
NetCommands.sendServerSettingsClient(conn, Settings.serverSettings.name, Settings.serverSettings.description, Settings.serverSettings.quickRespawn,
Settings.serverSettings.forceSpectators);
Settings.serverSettings.forceSpectators, Settings.serverSettings.competitiveMode);
NetCommands.setLobbyLevelIndexClient(conn, MPPlayMissionGui.currentCategoryStatic, MPPlayMissionGui.currentSelectionStatic);
// }
@ -749,7 +757,7 @@ class Net {
var gemSpawnPacket = new GemSpawnPacket();
gemSpawnPacket.deserialize(input);
if (MarbleGame.instance.world != null && !MarbleGame.instance.world._disposed) {
MarbleGame.instance.world.spawnHuntGemsClientSide(gemSpawnPacket.gemIds);
MarbleGame.instance.world.spawnHuntGemsClientSide(gemSpawnPacket.gemIds, gemSpawnPacket.expireds);
@:privateAccess MarbleGame.instance.world.gemPredictions.acknowledgeGemSpawn(gemSpawnPacket);
}

View file

@ -425,12 +425,13 @@ class NetCommands {
}
}
@:rpc(server) public static function sendServerSettings(name:String, desc:String, quickRespawn:Bool, forceSpectator:Bool) {
@:rpc(server) public static function sendServerSettings(name:String, desc:String, quickRespawn:Bool, forceSpectator:Bool, competitive:Bool) {
Net.connectedServerInfo = {
name: name,
description: desc,
quickRespawn: quickRespawn,
forceSpectator: forceSpectator
forceSpectator: forceSpectator,
competitiveMode: competitive
};
}
@ -452,4 +453,11 @@ class NetCommands {
}
MPPlayMissionGui.addChatMessage(msg);
}
@:rpc(server) public static function setCompetitiveTimerStartTicks(ticks:Int) {
if (MarbleGame.instance.world != null) {
var huntMode = cast(MarbleGame.instance.world.gameMode, HuntMode);
huntMode.setCompetitiveTimerStartTicks(ticks);
}
}
}

View file

@ -262,15 +262,19 @@ class ExplodableUpdatePacket implements NetPacket {
@:publicFields
class GemSpawnPacket implements NetPacket {
var gemIds:Array<Int>;
var expireds:Array<Bool>;
public function new() {
gemIds = [];
expireds = [];
}
public function serialize(b:OutputBitStream) {
b.writeInt(gemIds.length, 5);
for (gemId in gemIds) {
for (i in 0...gemIds.length) {
var gemId = gemIds[i];
b.writeInt(gemId, 11);
b.writeFlag(expireds[i]);
}
}
@ -278,6 +282,7 @@ class GemSpawnPacket implements NetPacket {
var count = b.readInt(5);
for (i in 0...count) {
gemIds.push(b.readInt(11));
expireds.push(b.readFlag());
}
}
}