extends RefCounted
## Screenshot scenes of the Knight's golden dragon (main.gd --shotmount=<mode>, with --shot / --shotat /
## --shotcam):  dragon = the dragon alone (rider hidden)  ride = a Knight on it, standing  walk = riding
## along  attack = a mounted spear thrust at a still monster.

const MountView = preload("res://scripts/mount_view.gd")


static func frames(main: Node, n: int) -> void:
	for i in n:
		await main.get_tree().process_frame


## The shot character becomes a Knight (through Swordsman at Job Lv 50) with Dragon Rider + a spear.
static func make_knight(main: Node) -> void:
	var ps = PlayerState
	ps.gain_exp(0, 100000)
	ps.change_job("swordman")
	ps.gain_exp(0, 50000000)
	ps.ch.skill_points = 0
	ps.ch.zeny += 100000
	ps.change_job("knight")
	ps.gain_exp(40000, 3000000)
	ps.ch.skills["riding"] = 1
	ps.ch.skills["cavalier_mastery"] = 5
	ps.equip("ashwood_pike")
	ps.hp = ps.max_hp()


static func run(main: Node, mode: String) -> void:
	# everyday mounts + Starwell (server/mounts.gd, ui/starwell_ui.gd): gacha | reveal | spot | stable | well |
	# lineup | ride:<id> | walk:<id>
	if mode in ["gacha", "reveal", "spot", "portal", "stable", "well", "lineup", "top"] or mode.begins_with("ride:") or mode.begins_with("walk:") or mode.begins_with("summon:"):
		await _mounts_shot(main, mode)
		return
	make_knight(main)
	var pl = main.player
	var pe = main.me()
	# the one-time class choice (Combat v2) would pop up over the shot
	if bool(PlayerState.ch.get("class_pick")):
		main.send({"t": "class_pick", "job": "defend_counter"})
	PlayerState.ch.class_pick = false
	if main.hud and main.hud.is_open("jobslots"):
		main.hud.toggle("jobslots")
	# nobody close by (and nobody attacks)
	for m in main.mobs():
		if m.global_position.distance_to(pl.global_position) < 9.0 and mode != "attack":
			m.map.remove_mob(m)
	await frames(main, 90)
	match mode:
		"dragon":
			var mv = MountView.new()
			pl.add_child(mv)
			mv.setup("golden_dragon")
			# standing: blend into the rest pose, wings mid-beat
			for i in 50:
				mv.animate(1.0 / 60.0, false)
				await main.get_tree().process_frame
			# the view re-shows its model every frame: shrink the rider away instead
			pl.model.scale = Vector3.ONE * 0.0001
			pl.name_label.visible = false
			pl.hp_bar.visible = false
		"ride", "walk", "attack":
			pe.acd = 0.0
			pe.cast_lock = 0.0
			main.send({"t": "skill", "id": "riding"})
			await frames(main, 80)
			if mode == "walk":
				main.send({"t": "move", "to": pl.global_position + Vector3(0, 0, 12)})
				await frames(main, 36)
			elif mode == "attack":
				var best = null
				var bd := 1e9
				for m in main.mobs():
					var d: float = m.global_position.distance_to(pl.global_position)
					if not m.dead and d < bd:
						bd = d
						best = m
				if best:
					best.atk_t = 1e6
					best.max_hp = 50000
					best.hp = 50000
					best.roam = 0.0
					best.wander_goal = best.global_position
					main.teleport_player(best.global_position + Vector3(-2.2, 0, 0))
					await frames(main, 20)
					main.send({"t": "attack", "target": best.id, "auto": true})
					await frames(main, 70)
	await frames(main, 12)


static func _mounts_shot(main: Node, mode: String) -> void:
	var Mounts = load("res://scripts/server/mounts.gd")
	var Starwell = load("res://scripts/server/starwell.gd")
	var ps = PlayerState
	var pl = main.player
	var pe = main.me()
	var c = ps.ch
	Mounts.ensure(c)
	# a collector: most mounts owned, some stars
	for id in ["bristle_hog", "bamboo_panda", "glade_stag", "frostmane_yeti", "sunflare_wyrmling", "azure_tidewyrm", "starborne_celestial", "aurelion"]:
		Mounts.add_copy(c, id)
	Mounts.add_copy(c, "glade_stag")
	Mounts.add_copy(c, "glade_stag")
	c.stable["shards"] = 340
	# the one-time class choice (Combat v2) would pop up over the shot
	if bool(c.get("class_pick")):
		main.send({"t": "class_pick", "job": "defend_counter"})
	c.class_pick = false
	for m in main.mobs():
		if m.global_position.distance_to(pl.global_position) < 12.0:
			m.map.remove_mob(m)
	await frames(main, 30)
	var hud = main.hud
	if hud.is_open("jobslots"):
		hud.toggle("jobslots")
	if mode.begins_with("summon:"):
		# mid-way through the generic summon (mount_fx.gd): flash, rising stars, the rider's hop
		var sid := mode.substr(mode.find(":") + 1)
		Mounts.add_copy(c, sid)
		main.send({"t": "mount_equip", "id": sid, "ride": false})
		await frames(main, 20)
		main.send({"t": "mount", "on": true})
		await frames(main, 7)
		return
	match mode:
		"gacha":
			hud.mount_ui.open_well()
			await frames(main, 90)
		"stable":
			hud.mount_ui.open_stable()
			hud.mount_ui.stable.sel = "aurelion"
			for a in OS.get_cmdline_user_args():
				if a.begins_with("--shotsel="):
					var sid: String = a.substr(10)
					Mounts.add_copy(c, sid)
					hud.mount_ui.stable.sel = sid
			hud.mount_ui.stable.refresh()
			await frames(main, 90)
		"reveal", "spot", "portal":
			var rng := RandomNumberGenerator.new()
			rng.seed = 20260930
			Starwell.rng_override = rng
			Starwell.pity(c, "mount")["m"] = 60
			hud.mount_ui.open_well()
			await frames(main, 20)
			# find a seed whose x10 shows a Mythic (the showcase pull)
			var r: Dictionary = {}
			for s in 400:
				rng.seed = 1000 + s
				var probe = load("res://scripts/server/character_state.gd").new()
				probe.from_dict(JSON.parse_string(JSON.stringify(c.to_dict())))
				var pr: Dictionary = Starwell.pull(probe, "mount", 10)
				if str(pr.get("best", "")) == "mythic":
					rng.seed = 1000 + s
					break
			r = main.send({"t": "star_pull", "well": "mount", "n": 10})
			hud.mount_ui.well.on_result(r)
			# the reveal runs on tweens (real time): wait in seconds, not frames
			if mode == "reveal":
				await main.get_tree().create_timer(7.5).timeout
				if hud.mount_ui.well._spot:
					hud.mount_ui.well._end_spot()
				await frames(main, 20)
			elif mode == "portal":
				await main.get_tree().create_timer(2.2).timeout
			else:
				await main.get_tree().create_timer(8.6).timeout
			Starwell.rng_override = null
		"well":
			var w := {}
			for n in GameData.npcs:
				if str(n.get("kind", "")) == "starwell" and str(n.get("map", GameData.start_map())) == main.map_id:
					w = n
			if not w.is_empty():
				main.teleport_player(Vector3(float(w["pos"][0]) - 4.0, 0, float(w["pos"][1]) + 3.0))
			await frames(main, 60)
		"top":
			# overview (camera from --shotcam), nothing opened
			await frames(main, 30)
		"lineup":
			# every mount in two rows facing the camera (the shot character hidden)
			var ids: Array = Mounts.defs().keys()
			main.rig.dist = 17.0
			main.rig.pitch = -16.0
			main.rig._apply()
			await frames(main, 5)
			var cam: Camera3D = main.get_viewport().get_camera_3d()
			var right: Vector3 = cam.global_transform.basis.x
			right.y = 0.0
			right = right.normalized()
			var fwd := Vector3(-cam.global_transform.basis.z.x, 0, -cam.global_transform.basis.z.z).normalized()
			var base: Vector3 = pl.global_position
			pl.model.scale = Vector3.ONE * 0.0001
			pl.name_label.visible = false
			pl.hp_bar.visible = false
			var n := ids.size()
			for i in n:
				var row := 0 if i < 6 else 1
				var k := i if row == 0 else i - 6
				var cnt := 6 if row == 0 else n - 6
				var v = load("res://scripts/mount_view.gd").new()
				main.add_child(v)
				v.global_position = base + right * ((k - (cnt - 1) * 0.5) * 3.3) + fwd * (4.0 if row == 0 else -1.5)
				v.setup(str(ids[i]))
				var to_cam: Vector3 = cam.global_position - v.global_position
				v.rotation.y = atan2(to_cam.x, to_cam.z) + 0.55
			for i in 40:
				await main.get_tree().process_frame
		_:
			var id := mode.substr(mode.find(":") + 1)
			Mounts.add_copy(c, id)
			main.send({"t": "mount_equip", "id": id, "ride": true})
			await frames(main, 60)
			if mode.begins_with("walk:"):
				main.send({"t": "move", "to": pl.global_position + Vector3(0, 0, 14)})
				await frames(main, 30)
	await frames(main, 6)
