mlpcardgame/Scenes/Scripts/Board.gd

312 lines
9.7 KiB
GDScript3
Raw Normal View History

2019-05-04 19:33:47 +00:00
extends Spatial
2019-05-05 23:35:58 +00:00
class_name Board
2019-06-01 21:52:16 +00:00
signal loaded()
onready var CardTemplate := preload("res://Scenes/Board/Card.tscn")
2019-05-04 19:33:47 +00:00
onready var camera := $Camera
2019-05-12 21:37:44 +00:00
onready var hand := $Camera/PlayerHand
onready var oppHand := $Camera/OppHand
onready var ui := $BoardUI
2019-05-05 19:19:39 +00:00
onready var cards := $Cards
2019-05-31 22:58:19 +00:00
export var mouseHandThreshold = 0.90
2019-05-05 19:19:39 +00:00
2019-05-06 11:12:57 +00:00
var holdingCard: Card = null
var focusedCard: Card = null
2019-05-07 18:56:26 +00:00
var currentZone: Stack = null
2019-05-04 19:33:47 +00:00
var mouseOrigin: Vector2
var lastCameraTransform: Transform
2019-06-01 21:52:16 +00:00
var cardsToLoad: int = 0
2019-05-07 18:56:26 +00:00
onready var zones: Dictionary = {
2019-05-12 21:37:44 +00:00
"player1.deck": $Cards/P1Deck,
"player1.banished": $Cards/P1Banished,
"player1.graveyard": $Cards/P1Graveyard,
"player1.problem": $Cards/P1Problem,
2019-05-12 21:37:44 +00:00
"player2.deck": $Cards/P2Deck,
"player2.banished": $Cards/P2Banished,
"player2.graveyard": $Cards/P2Graveyard,
"player2.problem": $Cards/P2Problem
2019-05-07 18:56:26 +00:00
}
2019-05-04 19:33:47 +00:00
func _ready():
2019-06-02 23:35:31 +00:00
# Load bgm
2019-07-06 00:15:43 +00:00
BGM.set_volume(BGM.LOW)
BGM.load_music("ingame3")
# Sample deck
var deck = [
"pr10","pr11","pr12","pr13","pr14","pr15","pr16","pr12","pr13","pr14",
"pr10","pr11","pr12","pr13","pr14","pr15","pr16","pr12","pr13","pr14",
"pr10","pr11","pr12","pr13","pr14","pr15","pr16","pr12","pr13","pr14",
"pr10","pr11","pr12","pr13","pr14","pr15","pr16","pr12","pr13","pr14",
"pr10","pr11","pr12","pr13","pr14","pr15","pr16","pr12","pr13","pr14"
]
2019-06-01 21:52:16 +00:00
cardsToLoad = len(deck)
# Fill deck with cards
for card in deck:
add_card(card, 0, "player1.deck")
#TODO Shuffle deck
#TODO Draw cards from deck
for i in range(0,6):
draw_card(0, "player1.deck")
2019-07-06 00:15:43 +00:00
for i in range(0,6):
draw_card(1, "player1.deck")
2019-05-04 19:33:47 +00:00
func _input(event: InputEvent):
# Camera zoom
if event.is_action("zoom_in"):
camera.zoom(true)
elif event.is_action("zoom_out"):
camera.zoom(false)
# Save original camera and mouse position before panning
if event.is_action_pressed("pan"):
mouseOrigin = get_viewport().get_mouse_position()
lastCameraTransform = camera.transform
2019-05-31 22:58:19 +00:00
var baseViewport := Vector2(1280, 720)
2019-05-04 21:42:07 +00:00
func _process(delta: float):
2019-05-05 19:19:39 +00:00
# If mouse is under a certain area then we're managing our hand
var absMousePos := get_viewport().get_mouse_position()
2019-05-04 19:33:47 +00:00
# If panning, translate mouse delta to camera delta
if Input.is_action_pressed("pan"):
2019-05-05 19:19:39 +00:00
var mouseDelta := absMousePos - mouseOrigin
var mousePos: Vector2 = mouseDelta * 0.0096 * (1-camera.get_zoom()/5) # Magic numbers everywhere
2019-05-05 19:19:39 +00:00
camera.transform.origin = lastCameraTransform.origin - Vector3(mousePos.x, 0, mousePos.y)
# If holding a card, move it between board/hand
if holdingCard != null:
2019-05-07 18:56:26 +00:00
# Check if we're selecting our hand while holding a card
2019-05-31 22:58:19 +00:00
var relPos := get_viewport().get_mouse_position() / baseViewport
2019-05-06 23:25:08 +00:00
var xMargin = 1.0-HAND_SCREEN_PERC
var selectingHand: bool = relPos.y > mouseHandThreshold and (relPos.x > xMargin/2 and relPos.x < (1.0-xMargin/2))
2019-05-07 18:56:26 +00:00
# Move card in/out hand
if selectingHand and not holdingCard.inHand:
holdingCard.inHand = true
call_deferred("reparent", holdingCard, cards, hand)
elif not selectingHand and holdingCard.inHand:
holdingCard.inHand = false
call_deferred("reparent", holdingCard, hand, cards)
2019-05-05 19:19:39 +00:00
2019-05-07 18:56:26 +00:00
# Move card to/from zone
if currentZone != null:
if holdingCard.inZone:
if holdingCard.zoneName != currentZone.zoneName:
# Move from old zone to new
call_deferred("reparent", holdingCard, zones[holdingCard.zoneName], currentZone)
holdingCard.zoneName = currentZone.zoneName
else:
if holdingCard.inHand:
call_deferred("reparent", holdingCard, hand, currentZone)
else:
call_deferred("reparent", holdingCard, cards, currentZone)
2019-05-26 22:00:15 +00:00
holdingCard.inZone = true
holdingCard.zoneName = currentZone.zoneName
2019-05-07 18:56:26 +00:00
elif holdingCard.inZone:
2019-05-26 11:23:22 +00:00
holdingCard.inZone = false
2019-05-07 18:56:26 +00:00
# Move from zone to hand/field
if holdingCard.inHand:
call_deferred("reparent", holdingCard, zones[holdingCard.zoneName], hand)
else:
call_deferred("reparent", holdingCard, zones[holdingCard.zoneName], cards)
holdingCard.zoneName = ""
2019-05-11 19:36:44 +00:00
holdingCard.flipped = false
2019-05-07 18:56:26 +00:00
func _physics_process(delta):
2019-06-02 23:35:31 +00:00
raycast_zone()
2019-05-07 18:56:26 +00:00
func _card_picked(card: Card):
2019-05-11 19:36:44 +00:00
# Call pop if applicable
if card.inZone:
zones[card.zoneName].pop_card()
holdingCard = card
holdingCard.animation.queue("lift")
2019-05-05 19:19:39 +00:00
func _card_dropped(card: Card):
if card.inHand:
card.animation.queue("drop-hand")
reorder_hand(0)
elif currentZone != null:
2019-05-11 19:36:44 +00:00
card.inZone = true
currentZone.push_card(card)
card.animation.queue("drop-board")
holdingCard = null
2019-05-06 11:12:57 +00:00
func _card_selected(card: Card):
if card.inHand and focusedCard == null:
focusedCard = card
card.handanim.play("focus")
2019-05-06 11:12:57 +00:00
func _card_unselected(card: Card):
if focusedCard == card:
focusedCard = null
2019-05-20 21:52:38 +00:00
if card.inHand:
card.handanim.play("blur")
func _card_moved(origin: Vector3, direction: Vector3, t: float, card: Card):
if card.flipped:
card.border.translation = Vector3(0, 0.01, 0)
card.border.rotation = Vector3(0, 0, PI)
else:
card.border.translation = Vector3.ZERO
card.border.rotation = Vector3.ZERO
if card.inHand:
card.translation = Vector3(direction.x * 3.0, 0.25, -0.25)
# Fix rotation if coming from hand
card.rotation = Vector3.ZERO
elif card.inZone:
2019-06-02 23:35:31 +00:00
card.translation = Vector3.UP * zones[card.zoneName].cards.size() * zones[card.zoneName].CARD_DISTANCE
2019-05-20 21:52:38 +00:00
card.rotation = Vector3.ZERO
else:
2019-06-02 23:35:31 +00:00
card.tween_move_to(origin + direction * t, 0.016)
2019-05-20 21:52:38 +00:00
# Rotate depending on what side of the board it is
2019-06-02 23:35:31 +00:00
var baseRot = card.rotation - Vector3.UP * card.rotation.y
2019-05-20 21:52:38 +00:00
if card.translation.z < 0:
2019-06-02 23:35:31 +00:00
card.tween_rotate(baseRot + Vector3.UP * PI, 0.04)
2019-05-20 21:52:38 +00:00
else:
2019-06-02 23:35:31 +00:00
card.tween_rotate(baseRot, 0.04)
card.tween()
2019-05-20 21:52:38 +00:00
func _card_clicked(card: Card):
if card.exhausted:
card.animation.queue("tap-back")
card.exhausted = false
else:
card.animation.queue("tap")
card.exhausted = true
func _card_menu_action(id: int, card: Card):
match id:
0: # Flip
if card.flipped:
card.animation.queue("flip-back")
card.flipped = false
else:
card.animation.queue("flip")
card.flipped = true
2019-05-05 19:19:39 +00:00
2019-06-01 21:52:16 +00:00
func _card_loaded():
cardsToLoad -= 1
if cardsToLoad <= 0:
emit_signal("loaded")
2019-05-05 19:19:39 +00:00
func reparent(object: Node, from: Node, to: Node):
from.remove_child(object)
to.add_child(object)
object.set_owner(to)
if to == hand or from == hand:
reorder_hand(0)
if to == oppHand or from == oppHand:
reorder_hand(1)
func add_card(cardID: String, playerID: int, zone: String):
var card := CardTemplate.instance()
card.cardID = cardID
card.playerID = playerID
card.inHand = zone == "hand"
card.connect("card_dropped", self, "_card_dropped", [card])
card.connect("card_picked", self, "_card_picked", [card])
card.connect("card_selected", ui, "_card_selected", [card])
card.connect("card_unselected", ui, "_card_unselected", [card])
2019-05-06 11:12:57 +00:00
card.connect("card_selected", self, "_card_selected", [card])
card.connect("card_unselected", self, "_card_unselected", [card])
card.connect("card_dropped_anim", self, "check_hand_drop", [card])
2019-05-20 21:52:38 +00:00
card.connect("card_moved", self, "_card_moved", [card])
card.connect("card_menu", ui, "show_card_menu", [card, self, "_card_menu_action"])
card.connect("card_clicked", self, "_card_clicked", [card])
2019-06-01 21:52:16 +00:00
card.connect("loaded", self, "_card_loaded")
if card.inHand:
2019-05-12 21:37:44 +00:00
# TODO support for >2 players
if playerID == 0:
hand.add_child(card)
else:
oppHand.add_child(card)
elif zones.has(zone):
card.inZone = true
card.zoneName = zone
zones[zone].add_child(card)
zones[zone].push_card(card)
else:
2019-05-06 11:12:57 +00:00
cards.add_child(card)
func draw_card(playerID: int, from: String):
var card := (zones[from] as Stack).pop_card()
card.inHand = true
card.inZone = false
card.flipped = false
card.animation.queue("flip-back-fast")
var target := hand
if playerID != 0:
target = oppHand
reparent(card, zones[from], target)
func check_hand_drop(card: Card):
# Re-order hand as soon as the animation is over
if card.inHand:
card.reset_transform()
2019-05-12 21:37:44 +00:00
reorder_hand(0)
2019-05-06 11:12:57 +00:00
const MAX_CARD_DISTANCE := 0.5
const HAND_SCREEN_PERC := 0.6
2019-05-06 11:12:57 +00:00
const CARD_ROTATION := 0.03
const UNITSPERPX := 0.003
2019-05-12 21:37:44 +00:00
func reorder_hand(playerID: int):
var cardsInHand: Array = []
if playerID == 0:
cardsInHand = hand.get_children()
else:
cardsInHand = oppHand.get_children()
2019-05-06 11:12:57 +00:00
cardsInHand.sort_custom(TransformSorter, "sort")
var size := cardsInHand.size()
2019-05-06 22:18:10 +00:00
# Check if we have nothing to order
if size < 1:
return
2019-05-06 11:12:57 +00:00
# Calculate total width of the player's hand and other things
# This is done in two ways, for small hands, MAX_CARD_DISTANCE is usually used
# as constant distance between cards, however, as the hand gets larger we don't
# want them to go offscreen, therefore a "maximum screen %" is used to determine
# how to fit all the cards within a % of the viewport's width
var distancePerc := get_viewport().size.x * HAND_SCREEN_PERC * UNITSPERPX / size
var distance := min(distancePerc, MAX_CARD_DISTANCE)
2019-05-06 11:12:57 +00:00
var totalWidth := distance * (size-1)
var minX := -totalWidth/2
2019-05-06 22:18:10 +00:00
2019-05-06 11:12:57 +00:00
# Iterate over all items, keep track of the index
var i := 0
for child in cardsInHand:
child.tween_move_to(Vector3(minX + distance * i, 0.02*i, -0.005*i))
child.tween_rotate(Vector3(0, CARD_ROTATION * (size/2.0 - i), 0))
2019-05-06 11:12:57 +00:00
i += 1
child.reset_transform()
child.tween()
2019-05-06 11:12:57 +00:00
2019-06-02 23:35:31 +00:00
func raycast_zone():
# Do some raycast magic that normal Godot events cannot get (like ignoring items)
var space_state := get_world().direct_space_state
var absMousePos := get_viewport().get_mouse_position()
var from: Vector3 = camera.project_ray_origin(absMousePos)
var to: Vector3 = from + camera.project_ray_normal(absMousePos) * 1000
var result := space_state.intersect_ray(from, to, [self, holdingCard], 0x7FFFFFFF, true, true)
if result:
if result.collider is Stack and result.collider.editable:
currentZone = result.collider
else:
currentZone = null
2019-05-06 11:12:57 +00:00
class TransformSorter:
static func sort(a, b):
if a.transform.origin.x < b.transform.origin.x:
return true
2019-05-11 19:36:44 +00:00
return false
func _card_picked_zone(card, zone):
_card_picked(card)