Last active
December 17, 2021 16:20
-
-
Save jjcf89/5b677ef6cd3a9d1ca706cc31f9bcf1cf to your computer and use it in GitHub Desktop.
Javascript to draw lines in Yucata's Oracle of Delphi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Draw paths of each ship through out the game | |
// | |
// Need to run code in console and replay the game | |
// Howto for Chrome: | |
// * Open finished game https://www.yucata.de/en/Game/Delphi/10373044#page | |
// * Click on first move in game log | |
// * Right click on page and select "Inspect" | |
// * Select the "Sources" tab and in left pane, change Page to Snippets | |
// * Create "+ New Snippet" and give it a name | |
// * Copy/paste this code into the snippet | |
// * Right click on snippet and select Run | |
// * (Optional) In Oracle window, go to General Settings tab and uncheck "show animations", this will speed up the replay | |
// * Go back to Oracle window and in the replay tab and press the "Start replay" play button | |
// * Wait till game has finished running, lines should be getting drawn behind the ships | |
// * Snippet will stay saved so you can run it again without having to recreate it | |
// | |
// Clearing lines: | |
// * You can clear the lines by running the following javascript in the console or in a different snippet | |
// ** $(".paths").remove(); $(".warp").remove() | |
// | |
// SNAP SVG Cheatsheet: https://gist.github.com/osvik/0185cb4381b35aad3d3e1f5438ca5ca4 | |
if (!y$.game.animateOld) { | |
y$.game.animateOld = y$.game.animate; | |
} | |
y$.game.animate = function(idx, action) { | |
// Override the animate function so we can draw our lines and skip the other animation effects to save time | |
var playerNum = y$.utils.getIdx(action.PID); | |
// Determine color of player | |
const playerColorIdx = y$.basegame.getColorIdx(playerNum); | |
const colorPicker = ["red", "green", "blue", "yellow"]; | |
const color = colorPicker[playerColorIdx]; | |
// Offset drawn lines so they don't overlap other ships | |
const offsetPicker = [ | |
{ x: 0, y: 20 }, // Red: Top left | |
{ x: 80, y: 10 }, // Green: Top Right | |
{ x: 0, y: 70 }, // Blue: Bot Left | |
{ x: 80, y: 80 }, // Yellow: Bot Right | |
]; | |
const offset = offsetPicker[playerColorIdx]; | |
// Select players ship | |
const ship = y$.game.snapBoard.select(".boardship" + playerNum); | |
// The total number of actions since start of game | |
const moveNum = action.MoveNr; | |
/* Track round number | |
* I can't find a round counter so lets make one | |
*/ | |
// Track the last move number so we can tell if we stopped and restarted | |
// We have to assume we restarted in the first round... | |
if (!ship.lastMove || moveNum < ship.lastMove) | |
{ | |
ship.roundNum = 1; | |
} | |
ship.lastMove = moveNum; | |
/** | |
* Draws a ships path as the ships move on the board | |
*/ | |
function addShipPath(startPosition, targetCoord, offX, offY, scale, easing, duration) { | |
var row = y$.game.Row(targetCoord); | |
var isEvenRow = row % 2 === 0; | |
// Get start position | |
const x1 = startPosition[0]; | |
const y1 = startPosition[1]; | |
// Get end position | |
const x2 = (y$.game.hexColumnToPixelColumn(y$.game.Column(targetCoord), isEvenRow) + offX); | |
const y2 = (y$.game.hexRowToPixelRow(row, isEvenRow) + offY); | |
// Ignore zero length moves | |
if (x1 != x2 || y1 != y2) { | |
switch (action.ActionId) { | |
case y$.game.Actions.PoseidonSelectTargetField: | |
// ship jumps via Poseidon | |
console.log(color + ": Warping from " + x1 + "," + y1 + " to " + x2 + "," + y2); | |
// TODO Not sure if there is some css which would center the text instead of doing it manually | |
const centerX = -20; | |
const centerY = 15; | |
y$.game.snapBoard.text(x1+offset["x"]+centerX, y1+offset["y"]+centerY, "🌌").addClass("warp"); | |
y$.game.snapBoard.text(x2+offset["x"]+centerX, y2+offset["y"]+centerY, "🌌").addClass("warp"); | |
// fall into normal move handling so we draw a line as well | |
case y$.game.Actions.SelectShipDestination: | |
// Normal ship movement - Draw line | |
console.log(color + ": Moving from " + x1 + "," + y1 + " to " + x2 + "," + y2); | |
line = y$.game.snapBoard.line(x1+offset["x"], y1+offset["y"], x2+offset["x"], y2+offset["y"]).attr({ | |
stroke: color, | |
}).addClass("paths"); | |
// Workaround: For some reason you can't add this using attr() | |
line.node.style['marker-end'] = "url(#arrow)" | |
// Track if we moved | |
ship.movedThisRound = true; | |
break; | |
} | |
} | |
// return end position, in pixels | |
return [x2, y2]; | |
}; | |
// Handle action | |
switch (action.ActionId) { | |
case y$.game.Actions.SelectShipDestination: | |
// Source from real animate function | |
// for (i = 1; action.ActionEffects.length >= 4 && i < action.ActionEffects[3].length; i++) { | |
// aSequence.push(y$.game.getAnimateObjectMoveToCoordFunction(y$.game.snapBoard.select('.boardship' + plrIdx), action.ActionEffects[3][i], 24, 39, 1, mina.easeout, 200)); | |
// } | |
// addAnimationOfOracleResourceCleanup(); | |
// Get start position, this is in pixels | |
var startPosition = [ship.matrix.e, ship.matrix.f]; | |
for (i = 1; action.ActionEffects.length >= 4 && i < action.ActionEffects[3].length; i++) { | |
startPosition = addShipPath(startPosition, action.ActionEffects[3][i], 24, 39, 1, mina.easeout, 200); | |
} | |
break; | |
case y$.game.Actions.PoseidonSelectTargetField: | |
// Source from real animate function | |
// aSequence.push(y$.game.getAnimateObjectMoveToCoordFunction(y$.game.snapBoard.select('.boardship' + plrIdx), action.ActionParams, 24, 39, 1, mina.linear, 600)); | |
// aSequence.push(y$.game.getAnimateGodStepFunction(plrIdx, y$.game.GOD.POSEIDON, false, y$.game.GODSTEPS.BOTTOM)); | |
// Get start position, this is in pixels | |
var startPosition = [ship.matrix.e, ship.matrix.f]; | |
addShipPath(startPosition, action.ActionParams, 24, 39, 1, mina.linear, 600); | |
break; | |
case y$.game.Actions.finishTurn: | |
// Write current round to ship position so we can track time | |
// but only if we've movedThisRound | |
if (ship.movedThisRound) { | |
delete ship.movedThisRound; | |
// TODO Not sure if there is some css which would center the text instead of doing it manually | |
const moveTextX = -20; | |
const moveTextY = 15; | |
y$.game.snapBoard.text(ship.matrix.e+offset["x"]+moveTextX, ship.matrix.f+offset["y"]+moveTextY, ship.roundNum).attr({ | |
stroke: color, | |
}).addClass("paths"); | |
} | |
// Next round | |
ship.roundNum++; | |
break; | |
} | |
// If set to skip animations, exit and resolve promise to skip timeout | |
if (!y$.userPreferences.getPreference('animated')) { | |
return $.Deferred().resolve().promise(); | |
} else { | |
return y$.game.animateOld(idx, action); | |
} | |
} | |
// arrowhead marker definition | |
marker = ` | |
<marker id="arrow" viewBox="0 0 10 5" refX="10" refY="2.5" | |
markerWidth="6" markerHeight="6" | |
orient="auto-start-reverse"> | |
<path d="M 0 0 L 10 2.5 L 0 5 z" /> | |
</marker>` | |
// Create element case sensitive | |
markElem = Snap.parse(marker) | |
$("#arrow").remove() | |
y$.game.snapBoard.append(markElem) | |
y$.game.snapBoard.select("#arrow").toDefs() | |
// Add css, delete the first two rules so if this is run multiple times, we only have one copy of these rules | |
// Note the first run does delete some unrelated css but at present they aren't used for anything... | |
/* 1 pixel black shadow to left, top, right and bottom */ | |
/* text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; */ | |
document.styleSheets[0].deleteRule(0); | |
document.styleSheets[0].deleteRule(1); | |
document.styleSheets[0].insertRule(".paths { stroke-width: 5px; font-size: 3em; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; }", 0); | |
document.styleSheets[0].insertRule(".warp { font-size: 2em; }", 0); |
Ran it on a 16 move game for the fun of it. From this post https://www.yucata.de/en/Forum?ForumID=4&postid=138475#138475
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If the game log of the sidebar has fully been rendered and is visible then you'll find elements like
<div class="move move3 action4">
under thediv
with id 'gameLog'. The action number is theaction.MoveNr
whereas the move number is round number you're looking for. Not for every action number you will find such a move element in the DOM ("out of round" actions don't have it). But for the ship navigation actions you should find it. So I suggest you process the DOM based on theaction.MoveNr
you have available.The implementation derives the move numbers in a similar way, see the definition of
var cntMoves
in the base functionupdateGameLog
: