diff --git a/frontend/src/pages/how-to-play/build-ship.mdx b/frontend/src/pages/how-to-play/build-ship.mdx index 31286ac..a4d1716 100644 --- a/frontend/src/pages/how-to-play/build-ship.mdx +++ b/frontend/src/pages/how-to-play/build-ship.mdx @@ -8,22 +8,23 @@ Creates a `ShipState` UTxO locking min ada and a `ShipToken` (minted in this tx)  -## Lucid Example +## Blaze Example -You can use the following Lucid script to build your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). +You can use the following Blaze script to build your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). -<CodeBlock content={`import { createShip } from "../src"; +<CodeBlock content={`import { Kupmios } from "@blaze-cardano/sdk"; +import { createShip } from "../src"; import { GameIdentifier, OutRef } from "../src/types"; +import { Unwrapped } from "@blaze-cardano/ogmios"; async function main() { const address = "addr_test1qzjpgxkhe06gxzstfhywg02ggy5ltuwne6mfr406dlf0mpwp9a07r34cwsnkpn44tllxuydw4wp0xvstw5jqv5q9lszsk2qynn"; - - const asteria_utxo: OutRef = { - tx_hash: - "a8c77645426fc3031f1daedb657dd1e9af03e2883576d694bcd188b653e91a28", - tx_index: 0n, - }; + + const provider = new Kupmios( + process.env.KUPO_URL!, + await Unwrapped.Ogmios.new(process.env.OGMIOS_URL!) + ); const spacetime_script_reference: OutRef = { tx_hash: @@ -47,13 +48,13 @@ async function main() { const pos_y = 20n; const gameIdentifier: GameIdentifier = { - asteria_utxo, spacetime_script_reference, pellet_script_reference, asteria_script_reference, }; const tx = await createShip( + provider, address, gameIdentifier, pos_x, diff --git a/frontend/src/pages/how-to-play/gather-fuel.mdx b/frontend/src/pages/how-to-play/gather-fuel.mdx index 6703fea..f2ff921 100644 --- a/frontend/src/pages/how-to-play/gather-fuel.mdx +++ b/frontend/src/pages/how-to-play/gather-fuel.mdx @@ -8,18 +8,24 @@ Updates the `fuel` datum field of the `ShipState` UTxO by adding the amount of f  -## Lucid Example +## Blaze Example -You can use the following Lucid script to gather fuel for your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). +You can use the following Blaze script to gather fuel for your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). -<CodeBlock content={`import { max } from "rxjs"; -import { GameIdentifier, OutRef } from "../src/types"; +<CodeBlock content={`import { GameIdentifier, OutRef } from "../src/types"; import { gatherFuel } from "../src"; +import { Unwrapped } from "@blaze-cardano/ogmios"; +import { Kupmios } from "@blaze-cardano/sdk"; async function main() { const address = "addr_test1qzjpgxkhe06gxzstfhywg02ggy5ltuwne6mfr406dlf0mpwp9a07r34cwsnkpn44tllxuydw4wp0xvstw5jqv5q9lszsk2qynn"; + const provider = new Kupmios( + process.env.KUPO_URL!, + await Unwrapped.Ogmios.new(process.env.OGMIOS_URL!) + ); + const ship_utxo: OutRef = { tx_hash: "3e04a7a3e4a1015705c44822feaf5f2da1e9609eebd68310c87b7eba7923739a", @@ -52,6 +58,7 @@ async function main() { }; const tx = await gatherFuel( + provider, address, gather_fuel_identifier, ); diff --git a/frontend/src/pages/how-to-play/mine-asteria.mdx b/frontend/src/pages/how-to-play/mine-asteria.mdx index 4b6f967..a5643e8 100644 --- a/frontend/src/pages/how-to-play/mine-asteria.mdx +++ b/frontend/src/pages/how-to-play/mine-asteria.mdx @@ -8,18 +8,23 @@ Updates the `AsteriaUtxo` by transferring its value to the ship owner's address.  -## Lucid Example +## Blaze Example -You can use the following Lucid script to mine Asteria with your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). +You can use the following Blaze script to mine Asteria with your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). -<CodeBlock content={`import { mineAsteria } from "../src"; +<CodeBlock content={`import { Unwrapped } from "@blaze-cardano/ogmios"; +import { Kupmios } from "@blaze-cardano/sdk"; +import { mineAsteria } from "../src"; import { GameIdentifier, OutRef } from "../src/types"; async function main() { const address = "addr_test1qzjpgxkhe06gxzstfhywg02ggy5ltuwne6mfr406dlf0mpwp9a07r34cwsnkpn44tllxuydw4wp0xvstw5jqv5q9lszsk2qynn"; - const admin_token_subject = - "d37ec8944834e0ce98d655820e05a2d0215b725fd2b79562128f79fa417374657269612041646d696e"; + + const provider = new Kupmios( + process.env.KUPO_URL!, + await Unwrapped.Ogmios.new(process.env.OGMIOS_URL!) + ); const spacetime_script_reference: OutRef = { tx_hash: @@ -38,11 +43,6 @@ async function main() { "39871aab15b7c5ab1075ba431d7475f3977fe40fbb8d654b6bdf6f6726659277", tx_index: 0n, }; - const asteria_utxo: OutRef = { - tx_hash: - "bbcdff5ed1c0dd78c58c42dab8356c161f865d0c3efc1ea281026140a0d38516", - tx_index: 1n, - }; const ship_utxo: OutRef = { tx_hash: @@ -52,7 +52,6 @@ async function main() { const gameIdentifier: GameIdentifier = { - asteria_utxo, ship_utxo, spacetime_script_reference, pellet_script_reference, @@ -60,6 +59,7 @@ async function main() { }; const tx = await mineAsteria( + provider, address, gameIdentifier, ); diff --git a/frontend/src/pages/how-to-play/move-ship.mdx b/frontend/src/pages/how-to-play/move-ship.mdx index 54afba9..5abfe22 100644 --- a/frontend/src/pages/how-to-play/move-ship.mdx +++ b/frontend/src/pages/how-to-play/move-ship.mdx @@ -8,16 +8,23 @@ Updates the `pos_x`, `pos_y` and `fuel` datum fields of the `ShipState` UTxO by  -## Lucid Example +## Blaze Example -You can use the following Lucid script to move your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). +You can use the following Blaze script to move your ship. The supporting files are available [here](https://github.com/txpipe/asteria/tree/main/offchain). -<CodeBlock content={`import { createShip, moveShip } from "../src"; +<CodeBlock content={`import { Unwrapped } from "@blaze-cardano/ogmios"; +import { Kupmios } from "@blaze-cardano/sdk"; +import { moveShip } from "../src"; import { OutRef, GameIdentifier } from "../src/types"; async function main() { const address = "addr_test1qzjpgxkhe06gxzstfhywg02ggy5ltuwne6mfr406dlf0mpwp9a07r34cwsnkpn44tllxuydw4wp0xvstw5jqv5q9lszsk2qynn"; + + const provider = new Kupmios( + process.env.KUPO_URL!, + await Unwrapped.Ogmios.new(process.env.OGMIOS_URL!) + ); const ship_utxo: OutRef = { tx_hash: @@ -47,6 +54,7 @@ async function main() { }; const tx = await moveShip( + provider, address, move_ship_identifier, delta_x, diff --git a/frontend/src/pages/leaderboard/index.tsx b/frontend/src/pages/leaderboard/index.tsx index 5a12874..ec91624 100644 --- a/frontend/src/pages/leaderboard/index.tsx +++ b/frontend/src/pages/leaderboard/index.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { useQuery, gql } from '@apollo/client'; -import { useChallengeStore } from '@/stores/challenge'; +import { useChallengeStore, Challenge } from '@/stores/challenge'; const PAGE_SIZE = 10; @@ -31,9 +31,14 @@ interface LeaderboardRecord { } interface RecordProps { + challenge: Challenge; record: LeaderboardRecord; } +const hexToAscii = (hex: string): string => { + return Buffer.from(hex, 'hex').toString(); +} + const getShipByAddress = (address: string): string => { const encoder = new TextEncoder(); const charCode = encoder.encode(address.charAt(address.length-3))[0]; @@ -41,21 +46,23 @@ const getShipByAddress = (address: string): string => { } const LeaderboardChip: React.FunctionComponent<RecordProps> = (props: RecordProps) => ( - <div className="flex-initial flex flex-row items-center mx-4 pl-8 pr-6 py-4 rounded-full bg-gradient-to-r from-[#46434312] to-[#FFFFFF12]"> - <h1 className="flex-initial pb-1 mr-6 font-monocraft-regular text-5xl text-[#07F3E6]"> - {props.record.ranking} - </h1> - <div className="flex-initial mr-6 p-4 rounded-full bg-[#FFFFFF08] border-b-[#333] border-b-solid border-b"> - <img className="w-8 h-8" src={getShipByAddress(props.record.address)} /> - </div> - <p className="flex-initial mr-6 font-dmsans-regular text-white"> - <span className="font-dmsans-bold">Pilot: </span> {props.record.pilotName.toUpperCase().slice(-6)}<br/> - <span className="font-dmsans-bold">Ship: </span> {props.record.shipName.toUpperCase().slice(-6)} - </p> - <div className="flex-initial py-2 px-4 rounded-full bg-[#E7ECEF] font-dmsans-regular text-[#171717]"> - {`${props.record.distance}km`} + <a href={`${props.challenge.explorerUrl}${props.record.address.replace('#0', '')}`} target="_blank"> + <div className="flex-initial flex flex-row items-center mx-4 pl-8 pr-6 py-4 rounded-full bg-gradient-to-r from-[#46434312] to-[#FFFFFF12]"> + <h1 className="flex-initial pb-1 mr-6 font-monocraft-regular text-5xl text-[#07F3E6]"> + {props.record.ranking} + </h1> + <div className="flex-initial mr-6 p-4 rounded-full bg-[#FFFFFF08] border-b-[#333] border-b-solid border-b"> + <img className="w-8 h-8" src={getShipByAddress(props.record.address)} /> + </div> + <p className="flex-initial mr-6 font-dmsans-regular text-white"> + <span className="font-dmsans-bold">Pilot: </span> {hexToAscii(props.record.pilotName)}<br/> + <span className="font-dmsans-bold">Ship: </span> {hexToAscii(props.record.shipName)} + </p> + <div className="flex-initial py-2 px-4 rounded-full bg-[#E7ECEF] font-dmsans-regular text-[#171717]"> + {`${props.record.distance}km`} + </div> </div> - </div> + </a> ); const LeaderboardRow: React.FunctionComponent<RecordProps> = (props: RecordProps) => ( @@ -65,13 +72,19 @@ const LeaderboardRow: React.FunctionComponent<RecordProps> = (props: RecordProps </td> <td className="p-4 font-dmsans-regular text-[#D7D7D7] text-left border border-[#333333]"> <img className="inline mr-4 w-8 h-8" src={getShipByAddress(props.record.address)} /> - <span>{props.record.address.replace('#0', '')}</span> + <a + className="text-[#07F3E6] underline" + href={`${props.challenge.explorerUrl}${props.record.address.replace('#0', '')}`} + target="_blank" + > + {props.record.address.replace('#0', '')} + </a> </td> <td className="p-4 font-dmsans-regular text-[#D7D7D7] text-left border border-[#333333]"> - {props.record.pilotName.toUpperCase().slice(-6)} + {hexToAscii(props.record.pilotName)} </td> <td className="p-4 font-dmsans-regular text-[#D7D7D7] text-left border border-[#333333]"> - {props.record.shipName.toUpperCase().slice(-6)} + {hexToAscii(props.record.shipName)} </td> <td className="p-4 font-dmsans-regular text-[#D7D7D7] text-left border border-[#333333]"> {props.record.fuel} @@ -139,7 +152,7 @@ export default function Leaderboard() { <div className="flex flex-row justify-center items-center mb-12"> {data && data.leaderboard && data.leaderboard.slice(0, 3).map(record => - <LeaderboardChip key={record.address} record={record} /> + <LeaderboardChip key={record.address} record={record} challenge={current()} /> )} </div> @@ -153,7 +166,7 @@ export default function Leaderboard() { </thead> <tbody> {getPageData().map(record => - <LeaderboardRow key={record.address} record={record} /> + <LeaderboardRow key={record.address} record={record} challenge={current()} /> )} </tbody> </table> diff --git a/frontend/src/pages/map/index.tsx b/frontend/src/pages/map/index.tsx index 08aec8b..07c1a1b 100644 --- a/frontend/src/pages/map/index.tsx +++ b/frontend/src/pages/map/index.tsx @@ -10,7 +10,8 @@ export default function Map() { ['fuelPolicyId', current().fuelPolicyId], ['shipAddress', current().shipAddress], ['fuelAddress', current().fuelAddress], - ['asteriaAddress', current().asteriaAddress] + ['asteriaAddress', current().asteriaAddress], + ['explorerUrl', current().explorerUrl], ]); return `/visualizer/index.html?${params.toString()}`; }; diff --git a/frontend/src/stores/challenge.ts b/frontend/src/stores/challenge.ts index 1d82dd4..f1917e1 100644 --- a/frontend/src/stores/challenge.ts +++ b/frontend/src/stores/challenge.ts @@ -8,6 +8,7 @@ export interface Challenge { fuelAddress: string; asteriaAddress: string; network: string; + explorerUrl: string; } export interface ChallengeStoreState { @@ -27,6 +28,7 @@ export const useChallengeStore = create<ChallengeStoreState>((set, get) => ({ fuelAddress: 'addr_test1wr7g448cgxqmshwqfaacc2vyky5jsnzwyuh0ghxkgszhtlgzrxj63', asteriaAddress: 'addr_test1wqdsuy97njefz53rkhd4v6a2kuqk0md5mrn996ygwekrdyq369wjg', network: 'preview', + explorerUrl: 'https://preview.cexplorer.io/tx/', }], current: () => get().challenges[get().selected], select: (index: number) => set(() => ({ selected: index })), diff --git a/godot-visualizer/scripts/global.gd b/godot-visualizer/scripts/global.gd index f4658a9..a53d9a3 100644 --- a/godot-visualizer/scripts/global.gd +++ b/godot-visualizer/scripts/global.gd @@ -7,6 +7,8 @@ var ships: Array[ShipData] = [] var fuels: Array[FuelData] = [] var asteria: AsteriaData = null +var explorer_url = "" + func init_data(data: Variant): ships = [] @@ -27,10 +29,10 @@ func init_data(data: Variant): )) if item["__typename"] == "Fuel": - fuels.append(FuelData.new(item["fuel"], position)) + fuels.append(FuelData.new(item["id"], item["fuel"], position)) if item["__typename"] == "Asteria": - asteria = AsteriaData.new(item["totalRewards"], position) + asteria = AsteriaData.new(item["id"], item["totalRewards"], position) func get_ships(): return ships @@ -47,6 +49,12 @@ func get_grid_size(): func get_cell_size(): return cell_size +func set_explorer_url(_explorer_url: String): + explorer_url = _explorer_url + +func get_explorer_url(): + return explorer_url + class ShipData: var id: String = "" @@ -64,18 +72,22 @@ class ShipData: class FuelData: + var id: String = "" var fuel: int = 0 var position: Vector2 = Vector2(0, 0) - func _init(_fuel: int, _position: Vector2): + func _init(_id: String, _fuel: int, _position: Vector2): + id = _id fuel = _fuel position = _position class AsteriaData: + var id: String = "" var totalRewards: int = 0 var position: Vector2 = Vector2(0, 0) - func _init(_totalRewards: int, _position: Vector2): + func _init(_id: String, _totalRewards: int, _position: Vector2): + id = _id totalRewards = _totalRewards position = _position diff --git a/godot-visualizer/scripts/main.gd b/godot-visualizer/scripts/main.gd index 1ca2f8f..599c78d 100644 --- a/godot-visualizer/scripts/main.gd +++ b/godot-visualizer/scripts/main.gd @@ -10,6 +10,7 @@ var fuel_policy_id = "" var ship_address = "" var fuel_address = "" var asteria_address = "" +var explorer_url = "" const headers = ["Content-Type: application/json"] const query = """ @@ -39,9 +40,11 @@ const query = """ } }, ... on Fuel { + id, fuel }, ... on Asteria { + id, totalRewards } } @@ -65,13 +68,16 @@ func _process(delta: float) -> void: $GUICanvasLayer/CenterContainer/Loader.rotation += delta * 10 -func _ready(): +func _ready(): api_url = JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('apiUrl')") shipyard_policy_id = JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('shipyardPolicyId')") fuel_policy_id = JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('fuelPolicyId')") ship_address = JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('shipAddress')") fuel_address = JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('fuelAddress')") asteria_address = JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('asteriaAddress')") + explorer_url = JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('explorerUrl')") + + Global.set_explorer_url(explorer_url) $HTTPRequest.request_completed.connect(_on_request_completed) fetch_data() @@ -120,14 +126,16 @@ func update_tooltip_position(position: Vector2) -> void: func _on_map_hide_tooltip() -> void: + Input.set_default_cursor_shape(Input.CURSOR_ARROW) $GUICanvasLayer/Tooltip.visible = false func _on_map_show_ship_tooltip(ship: Global.ShipData) -> void: + Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND) $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Title.text = "SHIP" $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label1.text = "Position | %d, %d" % [ship.position.x, ship.position.y] - $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label2.text = "Ship Token | %s" % ship.shipTokenName - $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label3.text = "Pilot Token | %s" % ship.pilotTokenName + $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label2.text = "Ship Token | %s" % ship.shipTokenName.hex_decode().get_string_from_utf8() + $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label3.text = "Pilot Token | %s" % ship.pilotTokenName.hex_decode().get_string_from_utf8() $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label4.text = "Fuel | %s" % ship.fuel $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label1.visible = true $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label2.visible = true @@ -137,6 +145,7 @@ func _on_map_show_ship_tooltip(ship: Global.ShipData) -> void: func _on_map_show_fuel_tooltip(fuel: Global.FuelData) -> void: + Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND) $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Title.text = "FUEL PELLET" $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label1.text = "Position | %d, %d" % [fuel.position.x, fuel.position.y] $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label2.text = "Fuel | %s" % fuel.fuel @@ -148,6 +157,7 @@ func _on_map_show_fuel_tooltip(fuel: Global.FuelData) -> void: func _on_map_show_asteria_tooltip(asteria: Global.AsteriaData) -> void: + Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND) $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Title.text = "ASTERIA" $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label1.text = "Position | %d, %d" % [asteria.position.x, asteria.position.y] $GUICanvasLayer/Tooltip/MarginContainer/VBoxContainer/Label2.text = "Total rewards | %s" % asteria.totalRewards diff --git a/godot-visualizer/scripts/map.gd b/godot-visualizer/scripts/map.gd index 9c6eea8..9d7f257 100644 --- a/godot-visualizer/scripts/map.gd +++ b/godot-visualizer/scripts/map.gd @@ -18,6 +18,8 @@ var mouse_entered_minimap = false var mouse_entered_minimap_control = false var mouse_entered_modal = false +var current_id = "" + func _on_main_dataset_updated() -> void: const center = Vector2(0, 0) @@ -42,6 +44,13 @@ func _on_main_dataset_updated() -> void: $Entities.add_child(asteria) +func _input(event): + if event is InputEventMouseButton and event.pressed and current_id != "": + JavaScriptBridge.eval("window.open('%s%s', '_blank')" % [ + Global.get_explorer_url(), current_id.split("#")[0] + ]) + + func _process(delta: float) -> void: var cell_size = Global.get_cell_size() var mouse_position = get_viewport().get_mouse_position() / $Camera.zoom @@ -58,17 +67,22 @@ func _process(delta: float) -> void: if asteria and asteria.position == cell_position: $Cell.animation = "filled" show_asteria_tooltip.emit(asteria) + current_id = asteria.id elif ships.size() > 0: $Cell.animation = "filled" show_ship_tooltip.emit(ships[0]) + current_id = ships[0].id elif fuels.size() > 0: $Cell.animation = "filled" show_fuel_tooltip.emit(fuels[0]) + current_id = fuels[0].id else: $Cell.animation = "empty" hide_tooltip.emit() + current_id = "" else: hide_tooltip.emit() + current_id = "" func is_mouse_hover_gui() -> bool: