add rewind options

This commit is contained in:
RandomityGuy 2023-05-13 21:49:05 +05:30
parent 1f7b117753
commit 497579089f
17 changed files with 138 additions and 62 deletions

View file

@ -43,6 +43,7 @@ typedef OptionsSettings = {
var marbleModel:String;
var marbleShader:String;
var rewindEnabled:Bool;
var rewindTimescale:Float;
}
typedef ControlsSettings = {
@ -122,6 +123,7 @@ class Settings {
marbleModel: "data/shapes/balls/ball-superball.dts",
marbleShader: "Default",
rewindEnabled: false,
rewindTimescale: 1,
vsync: #if js true #end
#if hl
false
@ -339,6 +341,8 @@ class Settings {
optionsSettings.fovX = 90;
if (optionsSettings.rewindEnabled == false #if js || optionsSettings.rewindEnabled == null #end)
optionsSettings.rewindEnabled = false;
if (optionsSettings.rewindTimescale == 0 #if js || optionsSettings.rewindTimescale == null #end)
optionsSettings.rewindTimescale = 1;
controlsSettings = json.controls;
if (json.touch != null) {
touchSettings = json.touch;
@ -383,6 +387,9 @@ class Settings {
if (optionsSettings.rewindEnabled == null) {
optionsSettings.rewindEnabled = false;
}
if (optionsSettings.rewindTimescale == null) {
optionsSettings.rewindTimescale = 1;
}
#end
highscoreName = json.highscoreName;
} else {

View file

@ -67,7 +67,6 @@ class EndGameGui extends GuiControl {
var nextLevelPreview = new GuiImage(tmpprevtile);
nextLevelPreview.position = new Vector(-15, 0);
nextLevelPreview.extent = new Vector(160, 110);
nextLevelPreview.doClipping = true;
nextLevel.addChild(nextLevelPreview);
mission.getNextMission().getPreviewImage(t -> {

View file

@ -37,7 +37,7 @@ class GuiButton extends GuiAnim {
}
public override function update(dt:Float, mouseState:MouseState) {
var renderRect = getRenderRectangle();
var renderRect = getHitTestRect();
if (renderRect.inRect(mouseState.position) && !disabled) {
if (buttonSounds && Key.isPressed(Key.MOUSE_LEFT)) {
AudioManager.playSound(ResourceLoader.getResource("data/sound/buttonpress.wav", ResourceLoader.getAudio, this.soundResources));
@ -73,8 +73,7 @@ class GuiButton extends GuiAnim {
}
}
if (!disabled) {
if (acceleratorWasPressed &&
(accelerator != 0 && hxd.Key.isReleased(accelerator)) || Gamepad.isReleased(gamepadAccelerator)) {
if (acceleratorWasPressed && (accelerator != 0 && hxd.Key.isReleased(accelerator)) || Gamepad.isReleased(gamepadAccelerator)) {
if (this.pressedAction != null) {
this.pressedAction(new GuiEvent(this));
}

View file

@ -1,5 +1,6 @@
package gui;
import format.abc.Data.ABCData;
import h2d.Flow;
import hxd.res.Image;
import h2d.Graphics;
@ -58,12 +59,20 @@ class GuiControl {
var _flow:Flow;
var _manualScroll = false;
// var _border:h2d.Graphics = null;
public function new() {}
public function render(scene2d:Scene, ?parent:Flow) {
if (this._flow == null) {
this._flow = new Flow(parent != null ? parent : scene2d);
// this._flow.debug = true;
}
// if (_border == null) {
// _border = new h2d.Graphics(scene2d);
// }
if (parent == null) {
if (scene2d.contains(this._flow)) {
scene2d.removeChild(this._flow);
@ -98,7 +107,10 @@ class GuiControl {
public function update(dt:Float, mouseState:MouseState) {
if (!_skipNextEvent) {
var hitTestRect = getHitTestRect();
var hitTestRect = getHitTestRect(!_manualScroll);
// _border.clear();
// _border.lineStyle(2, 0x0000FF);
// _border.drawRect(hitTestRect.position.x, hitTestRect.position.y, hitTestRect.extent.x, hitTestRect.extent.y);
if (hitTestRect.inRect(mouseState.position)) {
if (Key.isPressed(Key.MOUSE_LEFT)) {
mouseState.button = Key.MOUSE_LEFT;
@ -226,12 +238,21 @@ class GuiControl {
return rect;
}
public function getHitTestRect() {
public function getHitTestRect(useScroll:Bool = true) {
var thisRect = this.getRenderRectangle();
if (useScroll)
thisRect.position.y -= thisRect.scroll.y;
if (this.parent == null)
return thisRect;
else {
return thisRect.intersect(this.parent.getRenderRectangle());
var parRect = this.parent.getRenderRectangle();
// parRect.position.y -= parRect.scroll.y;
var rr = thisRect.intersect(parRect);
if (useScroll) {
thisRect.position.y -= thisRect.scroll.y;
rr.scroll.y = thisRect.scroll.y;
}
return rr;
}
}
@ -313,6 +334,7 @@ class GuiControl {
}
public function dispose() {
this._flow.remove();
for (c in this.children) {
c.dispose();
}

View file

@ -36,42 +36,12 @@ class GuiImage extends GuiControl {
}
super.render(scene2d, parent);
var renderRect = this.getRenderRectangle();
// var hittestRect = this.getHitTestRect();
// var obj:h2d.Object = bmp;
// if (doClipping) {
// bmpFlow = new Flow();
// bmpFlow.addChild(bmp);
// bmpFlow.overflow = FlowOverflow.Hidden;
// bmpFlow.multiline = true;
// bmpFlow.setPosition(hittestRect.position.x, hittestRect.position.y);
// obj = bmpFlow;
// }
// if (doClipping) {
// var fp = bmpFlow.getProperties(bmp);
// fp.offsetX = -Std.int(hittestRect.position.x - renderRect.position.x);
// fp.offsetY = -Std.int(hittestRect.position.y - renderRect.position.y);
// } else {
// bmp.setPosition(Math.floor(renderRect.position.x), Math.floor(renderRect.position.y));
// }
bmp.width = renderRect.extent.x;
bmp.height = renderRect.extent.y;
// if (doClipping) {
// bmpFlow.maxWidth = Std.int(hittestRect.extent.x);
// bmpFlow.maxHeight = Std.int(hittestRect.extent.y);
// }
// if (scene2d.contains(obj)) {
// scene2d.removeChild(obj); // Refresh "layer"
// }
// scene2d.addChild(obj);
}
public override function dispose() {
super.dispose();
if (this.doClipping) {
bmpFlow.remove();
} else
this.bmp.remove();
}

View file

@ -28,6 +28,7 @@ class GuiMLText extends GuiControl {
super();
this.text = new HtmlText(font);
this.text.loadFont = loadFontFunc;
this._manualScroll = true;
}
public override function render(scene2d:Scene, ?parent:h2d.Flow) {

View file

@ -12,6 +12,8 @@ import src.Util;
class GuiScrollCtrl extends GuiControl {
public var scrollY:Float = 0;
public var enabled:Bool = true;
public var childrenHandleScroll:Bool = false;
var maxScrollY:Float;
@ -35,6 +37,10 @@ class GuiScrollCtrl extends GuiControl {
var dirty:Bool = true;
var prevMousePos:Vector;
var _contentYPositions:Map<h2d.Object, Float> = [];
var deltaY:Float = 0;
public function new(scrollBar:Tile) {
super();
this.scrollTopTile = scrollBar.sub(0, 4, 10, 6);
@ -53,7 +59,7 @@ class GuiScrollCtrl extends GuiControl {
this.scrollBarY.scale(Settings.uiScale);
this.clickInteractive = new Interactive(10 * Settings.uiScale, 1);
this.clickInteractive.onPush = (e) -> {
if (!this.pressed) {
if (!this.pressed && enabled) {
this.pressed = true;
this.dirty = true;
this.updateScrollVisual();
@ -63,12 +69,14 @@ class GuiScrollCtrl extends GuiControl {
this.clickInteractive.startCapture(e2 -> {
if (e2.kind == ERelease) {
this.clickInteractive.stopCapture();
deltaY = 0;
}
if (e2.kind == EMove) {
if (prevEY == null) {
prevEY = e2.relY;
} else {
this.scrollY += (e2.relY - prevEY);
deltaY = (e2.relY - prevEY);
prevEY = e2.relY;
this.updateScrollVisual();
}
@ -77,6 +85,7 @@ class GuiScrollCtrl extends GuiControl {
if (this.pressed) {
this.pressed = false;
this.dirty = true;
deltaY = 0;
this.updateScrollVisual();
}
});
@ -112,6 +121,10 @@ class GuiScrollCtrl extends GuiControl {
updateScrollVisual();
super.render(scene2d, parent);
for (i in 0...this._flow.numChildren) {
var ch = this._flow.getChildAt(i);
_contentYPositions.set(ch, ch.y);
}
}
public function updateScrollVisual() {
@ -166,6 +179,17 @@ class GuiScrollCtrl extends GuiControl {
for (c in this.children) {
c.onScroll(0, scrollY * this.maxScrollY / renderRect.extent.y);
}
if (this._flow != null && !childrenHandleScroll) {
var actualDelta = deltaY;
if (scrollY != scrollYOld) {
actualDelta -= (scrollYOld - scrollY);
}
for (i in 0...this._flow.numChildren) {
var ch = this._flow.getChildAt(i);
ch.y -= cast actualDelta * this.maxScrollY / renderRect.extent.y;
}
}
}
public override function dispose() {

View file

@ -15,7 +15,7 @@ class GuiSlider extends GuiImage {
public var enabled:Bool = true;
public override function update(dt:Float, mouseState:MouseState) {
var renderRect = getRenderRectangle();
var renderRect = getHitTestRect();
if (renderRect.inRect(mouseState.position) && enabled) {
if (Key.isDown(Key.MOUSE_LEFT)) {
sliderValue = (mouseState.position.x - renderRect.position.x - bmp.width / 2) / renderRect.extent.x;

View file

@ -38,6 +38,7 @@ class GuiTextListCtrl extends GuiControl {
super();
this.font = font;
this.texts = texts;
this._manualScroll = true;
this.textObjs = [];
for (text in texts) {
var tobj = new Text(font);
@ -89,7 +90,7 @@ class GuiTextListCtrl extends GuiControl {
public override function render(scene2d:Scene, ?parent:h2d.Flow) {
var renderRect = this.getRenderRectangle();
var htr = this.getHitTestRect();
var htr = this.getHitTestRect(false);
if (parent != null) {
if (parent.contains(g))
@ -270,7 +271,7 @@ class GuiTextListCtrl extends GuiControl {
var renderRect = this.getRenderRectangle();
this.scroll = scrollY;
var hittestrect = this.getHitTestRect();
var hittestrect = this.getHitTestRect(false);
for (i in 0...textObjs.length) {
var text = textObjs[i];
text.y = Math.floor((i * (text.font.size + 4) + 5 + textYOffset * Settings.uiScale - scrollY));

View file

@ -66,6 +66,7 @@ class JukeboxDlg extends GuiImage {
var scroll = new GuiScrollCtrl(ResourceLoader.getResource("data/ui/common/philscroll.png", ResourceLoader.getImage, this.imageResources).toTile());
scroll.position = new Vector(51, 39);
scroll.extent = new Vector(439, 216);
scroll.childrenHandleScroll = true;
this.addChild(scroll);
var songCtrl = new GuiTextListCtrl(markerFelt24, songList);

View file

@ -68,7 +68,10 @@ class OptionsDlg extends GuiImage {
onlineBtn.extent = new Vector(134, 65);
window.addChild(onlineBtn);
var generalPanel:GuiScrollCtrl = null;
var applyFunc:Void->Void = () -> {
generalPanel.scrollY = 0;
Settings.applySettings();
};
@ -89,9 +92,10 @@ class OptionsDlg extends GuiImage {
}
window.addChild(applyBtn);
var generalPanel = new GuiControl();
generalPanel = new GuiScrollCtrl(ResourceLoader.getResource("data/ui/common/philscroll.png", ResourceLoader.getImage, this.imageResources).toTile());
generalPanel.position = new Vector(30, 88);
generalPanel.extent = new Vector(726, 394);
generalPanel.extent = new Vector(726, 364);
generalPanel.maxScrollY = 394;
window.addChild(generalPanel);
var currentTab = "general";
@ -130,6 +134,8 @@ class OptionsDlg extends GuiImage {
var dropdownparent = currentDropDown.parent;
currentDropDown.parent.removeChild(currentDropDown);
currentDropDown = null;
if (dropdownparent is GuiScrollCtrl)
cast(dropdownparent, GuiScrollCtrl).enabled = true;
haxe.Timer.delay(() -> setAllBtnState(true), 5); // delay this a bit to avoid update();
}
}
@ -147,6 +153,8 @@ class OptionsDlg extends GuiImage {
var optDropdownImg = new GuiImage(ResourceLoader.getResource('data/ui/options/dropdown-${size}.png', ResourceLoader.getImage, this.imageResources)
.toTile());
var dropDownYPos = yPos + 39;
optDropdownImg.position = new Vector(right ? 552 : 222, yPos + 39);
optDropdownImg.extent = new Vector(163, 79 + switch (size) {
case 'small': 0;
@ -163,16 +171,24 @@ class OptionsDlg extends GuiImage {
optDropdown.txtCtrl.text.textColor = 0;
optDropdown.pressedAction = (sender) -> {
if (currentDropDown == null) {
var pScroll = parent.getRenderRectangle();
optDropdownImg.position.y = dropDownYPos - pScroll.scroll.y;
parent.addChild(optDropdownImg);
optDropdownImg.render(MarbleGame.canvas.scene2d, @:privateAccess parent._flow);
currentDropDown = optDropdownImg;
if (parent is GuiScrollCtrl)
cast(parent, GuiScrollCtrl).enabled = false;
setAllBtnState(false);
return;
}
if (currentDropDown == optDropdownImg) {
parent.removeChild(optDropdownImg);
currentDropDown = null;
haxe.Timer.delay(() -> setAllBtnState(true), 5); // delay this a bit to avoid update();
haxe.Timer.delay(() -> {
setAllBtnState(true);
if (parent is GuiScrollCtrl)
cast(parent, GuiScrollCtrl).enabled = true;
}, 5); // delay this a bit to avoid update();
return;
}
}
@ -187,7 +203,7 @@ class OptionsDlg extends GuiImage {
case 'xlarge': 97;
default: 0;
});
// optDropdownList.scrollable = true;
optDropdownList.scrollable = true;
optDropdownList.textYOffset = -5;
optDropdownList.onSelectedFunc = (idx) -> {
onSelect(idx);
@ -224,7 +240,9 @@ class OptionsDlg extends GuiImage {
optSliders.push(optSlider);
}
makeOption("Screen Resolution:", () -> '${Settings.optionsSettings.screenWidth} x ${Settings.optionsSettings.screenHeight}', 18, generalPanel,
var yPos = 18;
makeOption("Screen Resolution:", () -> '${Settings.optionsSettings.screenWidth} x ${Settings.optionsSettings.screenHeight}', yPos, generalPanel,
"xlarge", [
"1024 x 800",
"1280 x 720",
@ -254,45 +272,71 @@ class OptionsDlg extends GuiImage {
Settings.optionsSettings.screenHeight = 1080;
}
});
makeOption("Screen Style:", () -> '${Settings.optionsSettings.isFullScreen ? "Full Screen" : "Windowed"}', 18, generalPanel, "small",
makeOption("Screen Style:", () -> '${Settings.optionsSettings.isFullScreen ? "Full Screen" : "Windowed"}', yPos, generalPanel, "small",
["Windowed", "Full Screen"], (idx) -> {
Settings.optionsSettings.isFullScreen = idx == 1;
}, true);
makeOption("Frame Rate:", () -> '${Settings.optionsSettings.frameRateVis ? "Visible" : "Hidden"}', 74, generalPanel, "small", ["Visible", "Hidden"],
yPos += 56;
makeOption("Frame Rate:", () -> '${Settings.optionsSettings.frameRateVis ? "Visible" : "Hidden"}', yPos, generalPanel, "small", ["Visible", "Hidden"],
(idx) -> {
Settings.optionsSettings.frameRateVis = idx == 0;
});
makeOption("OoB Insults:", () -> '${Settings.optionsSettings.oobInsults ? "Enabled" : "Disabled"}', 74, generalPanel, "small",
makeOption("OoB Insults:", () -> '${Settings.optionsSettings.oobInsults ? "Enabled" : "Disabled"}', yPos, generalPanel, "small",
["Disabled", "Enabled"], (idx) -> {
Settings.optionsSettings.oobInsults = idx == 1;
}, true);
makeOption("Free-Look:", () -> '${Settings.controlsSettings.alwaysFreeLook ? "Enabled" : "Disabled"}', 130, generalPanel, "small",
yPos += 56;
makeOption("Free-Look:", () -> '${Settings.controlsSettings.alwaysFreeLook ? "Enabled" : "Disabled"}', yPos, generalPanel, "small",
["Disabled", "Enabled"], (idx) -> {
Settings.controlsSettings.alwaysFreeLook = idx == 1;
});
makeOption("Invert Y:", () -> '${Settings.controlsSettings.invertYAxis ? "Yes" : "No"}', 130, generalPanel, "small", ["No", "Yes"], (idx) -> {
makeOption("Invert Y:", () -> '${Settings.controlsSettings.invertYAxis ? "Yes" : "No"}', yPos, generalPanel, "small", ["No", "Yes"], (idx) -> {
Settings.controlsSettings.invertYAxis = idx == 1;
}, true);
makeOption("Reflective Marble:", () -> '${Settings.optionsSettings.reflectiveMarble ? "Enabled" : "Disabled"}', 186, generalPanel, "small",
yPos += 56;
makeOption("Reflective Marble:", () -> '${Settings.optionsSettings.reflectiveMarble ? "Enabled" : "Disabled"}', yPos, generalPanel, "small",
["Disabled", "Enabled"], (idx) -> {
Settings.optionsSettings.reflectiveMarble = idx == 1;
});
makeOption("Vertical Sync:", () -> '${Settings.optionsSettings.vsync ? "Enabled" : "Disabled"}', 186, generalPanel, "small", ["Disabled", "Enabled"],
makeOption("Vertical Sync:", () -> '${Settings.optionsSettings.vsync ? "Enabled" : "Disabled"}', yPos, generalPanel, "small", ["Disabled", "Enabled"],
(idx) -> {
Settings.optionsSettings.vsync = idx == 1;
}, true);
makeSlider("Music Volume:", Settings.optionsSettings.musicVolume, 242, generalPanel, (val) -> {
yPos += 56;
makeOption("Rewind:", () -> '${Settings.optionsSettings.rewindEnabled ? "Enabled" : "Disabled"}', yPos, generalPanel, "small",
["Disabled", "Enabled"], (idx) -> {
Settings.optionsSettings.rewindEnabled = idx == 1;
}, false);
makeSlider("Rewind Speed:", (Settings.optionsSettings.rewindTimescale - 0.1) / (1 - 0.1), yPos, generalPanel, (val) -> {
Settings.optionsSettings.rewindTimescale = cast(0.1 + val * (1 - 0.1));
}, true);
yPos += 56;
makeSlider("Music Volume:", Settings.optionsSettings.musicVolume, yPos, generalPanel, (val) -> {
Settings.optionsSettings.musicVolume = val;
AudioManager.updateVolumes();
});
makeSlider("Sound Volume:", Settings.optionsSettings.soundVolume, 242, generalPanel, (val) -> {
makeSlider("Sound Volume:", Settings.optionsSettings.soundVolume, yPos, generalPanel, (val) -> {
Settings.optionsSettings.soundVolume = val;
AudioManager.updateVolumes();
}, true);
makeSlider("Field of View:", (Settings.optionsSettings.fovX - 60) / (140 - 60), 298, generalPanel, (val) -> {
yPos += 56;
makeSlider("Field of View:", (Settings.optionsSettings.fovX - 60) / (140 - 60), yPos, generalPanel, (val) -> {
Settings.optionsSettings.fovX = cast(60 + val * (140 - 60));
});
makeSlider("Mouse Speed:", (Settings.controlsSettings.cameraSensitivity - 0.2) / (3 - 0.2), 298, generalPanel, (val) -> {
makeSlider("Mouse Speed:", (Settings.controlsSettings.cameraSensitivity - 0.2) / (3 - 0.2), yPos, generalPanel, (val) -> {
Settings.controlsSettings.cameraSensitivity = cast(0.2 + val * (3 - 0.2));
}, true);
@ -319,6 +363,8 @@ class OptionsDlg extends GuiImage {
return "Use PowerUp";
if (Settings.controlsSettings.freelook == key && bindingName != "Free Look")
return "Free Look";
if (Settings.controlsSettings.rewind == key && bindingName != "Rewind")
return "Rewind";
return null;
}
@ -409,12 +455,16 @@ class OptionsDlg extends GuiImage {
MarbleGame.canvas.setContent(new TouchCtrlsEditGui());
}
hotkeysPanel.addChild(remapBtn);
} else {
makeRemapOption("Rewind:", 326, Util.getKeyForButton2(Settings.controlsSettings.rewind), (key) -> Settings.controlsSettings.rewind = key,
hotkeysPanel, true);
}
generalBtn.pressedAction = (e) -> {
if (currentTab != "general") {
currentTab = "general";
hotkeysPanel.parent.removeChild(hotkeysPanel);
generalPanel.scrollY = 0;
window.addChild(generalPanel);
MarbleGame.canvas.render(MarbleGame.canvas.scene2d); // Force refresh
}

View file

@ -185,7 +185,6 @@ class PlayGui {
.toTile());
timerTransparency.position = new Vector(14, -7);
timerTransparency.extent = new Vector(228, 71);
timerTransparency.doClipping = false;
timerCtrl.addChild(timerTransparency);
timerNumbers[0].position = new Vector(23, 0);
@ -463,7 +462,6 @@ class PlayGui {
blastFill = new GuiImage(ResourceLoader.getResource("data/ui/game/blastbar_bargreen.png", ResourceLoader.getImage, this.imageResources).toTile());
blastFill.position = new Vector(5, 5);
blastFill.extent = new Vector(58, 17);
blastFill.doClipping = false;
blastBar.addChild(blastFill);
blastFrame = new GuiImage(ResourceLoader.getResource("data/ui/game/blastbar.png", ResourceLoader.getImage, this.imageResources).toTile());

View file

@ -100,6 +100,7 @@ class ReplayCenterGui extends GuiImage {
var scrollCtrl = new GuiScrollCtrl(ResourceLoader.getResource("data/ui/common/philscroll.png", ResourceLoader.getImage, this.imageResources).toTile());
scrollCtrl.position = new Vector(30, 25);
scrollCtrl.extent = new Vector(283, 346);
scrollCtrl.childrenHandleScroll = true;
wnd.addChild(scrollCtrl);
var arial14fontdata = ResourceLoader.getFileEntry("data/font/arial.fnt");

View file

@ -164,6 +164,7 @@ class SearchGui extends GuiImage {
scrollCtrl = new GuiScrollCtrl(ResourceLoader.getResource("data/ui/common/philscroll.png", ResourceLoader.getImage, this.imageResources).toTile());
scrollCtrl.position = new Vector(19, 65);
scrollCtrl.extent = new Vector(447, 317);
scrollCtrl.childrenHandleScroll = true;
this.addChild(scrollCtrl);
searchMissionList = new GuiTextListCtrl(markerFelt24, displayList);

View file

@ -8,6 +8,7 @@ import shapes.Trapdoor;
import shapes.PushButton;
import src.Util;
import shapes.Nuke;
import src.Settings;
class RewindManager {
var frames:Array<RewindFrame> = [];
@ -17,6 +18,7 @@ class RewindManager {
public function new(level:MarbleWorld) {
this.level = level;
this.timeScale = Settings.optionsSettings.rewindTimescale;
}
public function recordFrame() {

View file

@ -115,7 +115,7 @@ class MovementInputEdit extends GuiGraphics {
state = 0;
}
public override function getHitTestRect() {
public override function getHitTestRect(useScroll:Bool = true) {
var thisRect = this.getRenderRectangle();
if (selected) {

View file

@ -44,7 +44,7 @@ class TouchEditButton extends GuiGraphics {
this.radius = radius;
}
public override function getHitTestRect() {
public override function getHitTestRect(useScroll:Bool = true) {
var thisRect = this.getRenderRectangle();
thisRect.position = thisRect.position.sub(new Vector(radius, radius));