~sjm/well-kard

d1f49e926c19ad79aeec637773c0cf37695b1958 — Sam Marshall 3 years ago 253ace0 main
get basic gameplay in place

This implements movement, a draw pile, discard pile, hand. The ability
to play cards, rudimentary card effects, enemies, time passing or not.

There's of course a long way to go here, even in the basic gameplay
loop.

The player needs health, there needs to be indicators for what is
going on to make everything less confusing. Cards need descriptions
and to be much clearer in general. Enemies need to drop loot, the
player needs a deck management screen and an inventory. The list goes
on! But the list is shorter than it was.
A .import/card-back.png-9322ffe79ce1d73ce4eadd6dbacce4bd.md5 => .import/card-back.png-9322ffe79ce1d73ce4eadd6dbacce4bd.md5 +3 -0
@@ 0,0 1,3 @@
source_md5="fd072e06a3e8d82016622b8f8cc21607"
dest_md5="8273e6adac9462958fc44c887dfd5d8a"


A .import/card-back.png-9322ffe79ce1d73ce4eadd6dbacce4bd.stex => .import/card-back.png-9322ffe79ce1d73ce4eadd6dbacce4bd.stex +0 -0
A .import/card-frame.png-dcba6c637aaecb475a4958d4081a3eae.md5 => .import/card-frame.png-dcba6c637aaecb475a4958d4081a3eae.md5 +3 -0
@@ 0,0 1,3 @@
source_md5="5b593e0795835694435467f6a13872a1"
dest_md5="bf2ea6ca219c19e68c7e62e1b0472d0f"


A .import/card-frame.png-dcba6c637aaecb475a4958d4081a3eae.stex => .import/card-frame.png-dcba6c637aaecb475a4958d4081a3eae.stex +0 -0
A Card.gd => Card.gd +12 -0
@@ 0,0 1,12 @@
extends Node2D

class_name Card

export (int) var num
export (String) var title

var effect
var type

func _ready():
	$Sprite/CardName.text = title

A Card.tscn => Card.tscn +35 -0
@@ 0,0 1,35 @@
[gd_scene load_steps=6 format=2]

[ext_resource path="res://assets/Patrick_Hand/PatrickHand-Regular.ttf" type="DynamicFontData" id=1]
[ext_resource path="res://assets/card-frame.png" type="Texture" id=2]
[ext_resource path="res://Card.gd" type="Script" id=3]

[sub_resource type="StyleBoxFlat" id=1]
bg_color = Color( 0.6, 0.6, 0.6, 0 )

[sub_resource type="DynamicFont" id=2]
size = 42
outline_color = Color( 0, 0, 0, 1 )
font_data = ExtResource( 1 )

[node name="Card" type="Node2D"]
script = ExtResource( 3 )

[node name="Sprite" type="Sprite" parent="."]
texture = ExtResource( 2 )

[node name="CardName" type="Label" parent="Sprite"]
anchor_left = 0.5
anchor_right = 0.5
margin_left = -644.079
margin_top = -539.362
margin_right = -36.0793
margin_bottom = -420.362
custom_styles/normal = SubResource( 1 )
custom_fonts/font = SubResource( 2 )
custom_colors/font_color = Color( 0, 0, 0, 1 )
text = "Attack
"
__meta__ = {
"_edit_use_anchors_": false
}

A Hand.gd => Hand.gd +100 -0
@@ 0,0 1,100 @@
extends Node

signal request_draw
signal discard(card_name)
signal played(effect)


var Effect = preload("res://cards/effect.gd")
var Card = preload("res://Card.tscn")
var cards = []


func _ready():
	pass


func _process(delta):
	# TODO: perform the card's effect
	if Input.is_action_just_pressed("card_one"):
		remove(0)
	if Input.is_action_just_pressed("card_two"):
		remove(1)
	if Input.is_action_just_pressed("card_three"):
		remove(2)
	if Input.is_action_just_pressed("card_four"):
		remove(3)
	if Input.is_action_just_pressed("card_five"):
		remove(4)


func draw():
	if cards.size() < 5:
		emit_signal("request_draw")


func handle_Draw(card):
	# TODO: define these elsewhere
	match card:
		"Defend":
			var c = Card.instance()
			c.title = "Defend"
			c.effect = Effect.defend(5)
			cards.append(c)
		"Attack":
			var c = Card.instance()
			c.title = "Attack"
			c.effect = Effect.attack(4)
			cards.append(c)

	display_cards()


func display_cards():
	for child in get_children():
		remove_child(child)

	var viewport_size = get_viewport().size
	var bottom_center = Vector2(viewport_size.x / 2, viewport_size.y)

	var shift = Vector2(0, 0)
	var node_width = 0
	var i = 0
	var card_width = 0

	for card in cards:
		card.num = i + 1

		var card_sprite = card.get_node("Sprite")
		card_width = card_sprite.get_rect().size.x / 2

		shift.x = (card_width * i)
		card.position = shift
		node_width = (card_width * i) + card_width

		add_child(card)
		i += 1

	var width = card_width * cards.size()

	self.position = Vector2(bottom_center.x - (width / 2), bottom_center.y - 20)


func remove(index):
	var i = 0
	var cs = []
	for card in cards:
		if i == index:
			emit_signal("discard", card.title)
			emit_signal("played", card.effect)
			card.queue_free()
		else:
			cs.push_back(card)

		i += 1
	cards = cs
	display_cards()


func _on_Deck_draw_card(card):
	handle_Draw(card)

A Main.gd => Main.gd +35 -0
@@ 0,0 1,35 @@
extends Node

var Enemy = preload("./enemy/Enemy.tscn")

var player_moving = false
var time_player_moving = 0
var ticks_without_enemy = 0
var enemies = []

func _ready():
	$Player.start(get_viewport().size / 2)


func _process(delta):
	if player_moving:
		time_player_moving += delta

	if time_player_moving > 2:
		$Hand.draw()
		time_player_moving = 0
		ticks_without_enemy += 1
		tick()


func tick():
	if ticks_without_enemy > 0 and randi() % 2 == 1:
		ticks_without_enemy = 0
		var e = Enemy.instance()

		enemies.push_back(e)
		add_child(e)

	
func _on_Player_move(ismoving):
	player_moving = ismoving

A Main.gd~ => Main.gd~ +16 -0
@@ 0,0 1,16 @@
extends Node


# Declare member variables here. Examples:
# var a = 2
# var b = "text"


# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
#	pass

A Main.tscn => Main.tscn +27 -0
@@ 0,0 1,27 @@
[gd_scene load_steps=6 format=2]

[ext_resource path="res://Player.tscn" type="PackedScene" id=1]
[ext_resource path="res://Main.gd" type="Script" id=2]
[ext_resource path="res://Hand.gd" type="Script" id=3]
[ext_resource path="res://deck/Deck.tscn" type="PackedScene" id=4]
[ext_resource path="res://cards/DiscardPile.tscn" type="PackedScene" id=5]

[node name="Main" type="Node"]
script = ExtResource( 2 )

[node name="Player" parent="." instance=ExtResource( 1 )]

[node name="Hand" type="Node2D" parent="."]
script = ExtResource( 3 )

[node name="Deck" parent="." instance=ExtResource( 4 )]

[node name="DiscardPile" parent="." instance=ExtResource( 5 )]

[connection signal="move" from="Player" to="." method="_on_Player_move"]
[connection signal="discard" from="Hand" to="DiscardPile" method="_on_Hand_discard"]
[connection signal="played" from="Hand" to="Player" method="_on_Hand_played"]
[connection signal="request_draw" from="Hand" to="Deck" method="_on_Hand_request_draw"]
[connection signal="draw_card" from="Deck" to="Hand" method="_on_Deck_draw_card"]
[connection signal="empty" from="Deck" to="DiscardPile" method="_on_Deck_empty"]
[connection signal="empty" from="DiscardPile" to="Deck" method="_on_DiscardPile_empty"]

M Player.gd => Player.gd +50 -18
@@ 1,34 1,66 @@
extends RigidBody2D

var target = Vector2()
export var speed = 400
# Declare member variables here. Examples:
# var a = 2
# var b = "text"
signal move(ismoving)
signal attacked(damage, attack_distance)

func start(pos):
	position = pos
	target = pos
	
func _input(event):
	if event is InputEventMouseButton and event.pressed:
		target = event.position
		print_debug()
var Effect = preload("res://cards/effect.gd")

export var speed = 400
var target = Vector2()
var moving = false
var attacking = false
var attack_distance = 300
var time_moving = false
var attack_timer

# Called when the node enters the scene tree for the first time.
func _ready():
	target = position
	attack_timer = $AttackTimer

	attack_timer.connect("timeout", self, "_on_Attack_Timer_timeout")

func _process(delta):
	var velocity = Vector2()
	var direction = Vector2()
	

	time_moving = attacking or moving

	if position.distance_to(target) > 10:
		direction = target - position
	
		if moving == false:
			moving = true
			emit_signal("move", true)
	elif moving == true:
		emit_signal("move", false)
		moving = false

	if direction.length() > 0:
		velocity = direction.normalized() * speed 
		velocity = direction.normalized() * speed
		$Sprite.rotation = Vector2.UP.angle_to(direction)
		

	position += velocity * delta

func _input(event):
	if event is InputEventMouseButton and event.pressed:
		target = event.position


func start(pos):
	position = pos
	target = pos


func attack(amt):
	attacking = true
	attack_timer.start()
	emit_signal("attacked", amt, attack_distance)


func _on_Hand_played(effect):
	var atk = Effect.attack_amt(effect)

	if atk > 0:
		attack(atk)

func _on_Attack_Timer_timeout():
	attacking = false

M Player.tscn => Player.tscn +6 -1
@@ 13,8 13,13 @@ script = ExtResource( 2 )
speed = 800

[node name="Sprite" type="Sprite" parent="."]
scale = Vector2( 0.6, 0.6 )
texture = ExtResource( 1 )

[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
[node name="Body" type="CollisionShape2D" parent="."]
rotation = 1.5708
scale = Vector2( 0.6, 0.6 )
shape = SubResource( 1 )

[node name="AttackTimer" type="Timer" parent="."]
one_shot = true

A assets/Patrick_Hand/OFL.txt => assets/Patrick_Hand/OFL.txt +93 -0
@@ 0,0 1,93 @@
Copyright (c) 2010-2012 Patrick Wagesreiter (mail@patrickwagesreiter.at)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

A assets/Patrick_Hand/PatrickHand-Regular.ttf => assets/Patrick_Hand/PatrickHand-Regular.ttf +0 -0
A assets/card-frame.png => assets/card-frame.png +0 -0
A assets/card-frame.png.import => assets/card-frame.png.import +34 -0
@@ 0,0 1,34 @@
[remap]

importer="texture"
type="StreamTexture"
path="res://.import/card-frame.png-dcba6c637aaecb475a4958d4081a3eae.stex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://assets/card-frame.png"
dest_files=[ "res://.import/card-frame.png-dcba6c637aaecb475a4958d4081a3eae.stex" ]

[params]

compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

A cards/DiscardPile.gd => cards/DiscardPile.gd +37 -0
@@ 0,0 1,37 @@
extends Node2D

signal empty(cards)

var cards = []


func _ready():
	position()


func _process(delta):
	$Count.text = str(cards.size())
	position()


func position():
	var bottom_right = get_viewport().size

	var label_rect = $Label.get_rect()
	var count_rect = $Count.get_rect()

	var rect = label_rect.merge(count_rect)

	var pos = bottom_right - rect.size + Vector2(-60, 0)

	self.position = pos


func _on_Hand_discard(card):
	cards.append(card)


func _on_Deck_empty():
	cards.shuffle()
	emit_signal("empty", cards)
	cards = []

A cards/DiscardPile.tscn => cards/DiscardPile.tscn +32 -0
@@ 0,0 1,32 @@
[gd_scene load_steps=4 format=2]

[ext_resource path="res://cards/DiscardPile.gd" type="Script" id=1]
[ext_resource path="res://assets/Patrick_Hand/PatrickHand-Regular.ttf" type="DynamicFontData" id=2]

[sub_resource type="DynamicFont" id=1]
size = 64
font_data = ExtResource( 2 )

[node name="DiscardPile" type="Node2D"]
script = ExtResource( 1 )

[node name="Label" type="Label" parent="."]
margin_right = 169.0
margin_bottom = 87.0
custom_fonts/font = SubResource( 1 )
text = "Discard:"
__meta__ = {
"_edit_use_anchors_": false
}

[node name="Count" type="Label" parent="."]
margin_left = 196.0
margin_top = 3.0
margin_right = 236.0
margin_bottom = 180.0
custom_fonts/font = SubResource( 1 )
text = "0
"
__meta__ = {
"_edit_use_anchors_": false
}

A cards/effect.gd => cards/effect.gd +24 -0
@@ 0,0 1,24 @@
static func attack(amt: int):
	return {"attack": amt}

static func defend(amt: int):
	return {"defend": amt}

static func effect(effects):
	var out = {}
	for eff in effects:
		for key in eff.keys():
			out[key] = eff[key]

	return out

static func is_attack(eff):
	return eff.has("attack")

static func attack_amt(eff):
	print_debug(eff)
	print_debug(is_attack(eff))
	if is_attack(eff):
		return eff.attack
	else:
		return 0

A deck/Deck.gd => deck/Deck.gd +44 -0
@@ 0,0 1,44 @@
extends Node

# a card is to be drawn
signal draw_card(card)
# the deck is empty
signal empty

var cards = []


func add(cs):
	cards.append(cs)


func _ready():
	var viewport_size = get_viewport().size
	var bottom_left = Vector2(0, viewport_size.y)
	var me_rect = $Count.get_rect().merge($Sprite.region_rect)

	cards = ["Defend", "Defend", "Defend", "Attack", "Attack", "Attack", "Attack"]

	cards.shuffle()

	self.position = bottom_left + me_rect.position + Vector2(me_rect.size.x + 100, 0 - 50)


func _on_DiscardPile_empty(cs):
	cards = cs


func _on_Hand_request_draw():
	if cards.size() == 0:
		emit_signal("empty")
	else:
		emit_signal("draw_card", cards.pop_front())


func _process(delta):
	$Count.text = str(cards.size())

	if cards.size() == 0:
		$Sprite.hide()
	else:
		$Sprite.show()

A deck/Deck.tscn => deck/Deck.tscn +30 -0
@@ 0,0 1,30 @@
[gd_scene load_steps=5 format=2]

[ext_resource path="res://deck/Deck.gd" type="Script" id=1]
[ext_resource path="res://deck/card-back.png" type="Texture" id=2]
[ext_resource path="res://assets/Patrick_Hand/PatrickHand-Regular.ttf" type="DynamicFontData" id=3]

[sub_resource type="DynamicFont" id=1]
size = 64
font_data = ExtResource( 3 )

[node name="Deck" type="Node2D"]
script = ExtResource( 1 )

[node name="Sprite" type="Sprite" parent="."]
scale = Vector2( 0.5, 0.5 )
texture = ExtResource( 2 )

[node name="Count" type="Label" parent="."]
anchor_left = 1.0
anchor_right = 1.0
margin_left = 107.914
margin_top = -374.939
margin_right = 147.914
margin_bottom = -287.939
custom_fonts/font = SubResource( 1 )
text = "0"
align = 2
__meta__ = {
"_edit_use_anchors_": false
}

A deck/card-back.png => deck/card-back.png +0 -0
A deck/card-back.png.import => deck/card-back.png.import +34 -0
@@ 0,0 1,34 @@
[remap]

importer="texture"
type="StreamTexture"
path="res://.import/card-back.png-9322ffe79ce1d73ce4eadd6dbacce4bd.stex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://deck/card-back.png"
dest_files=[ "res://.import/card-back.png-9322ffe79ce1d73ce4eadd6dbacce4bd.stex" ]

[params]

compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

A enemy/Enemy.gd => enemy/Enemy.gd +35 -0
@@ 0,0 1,35 @@
extends Node2D

var SPEED = 200
var HP = 8

var player
var target
var time_moving = false

# Called when the node enters the scene tree for the first time.
func _ready():
	player = get_node("../Player")
	player.connect("attacked", self, "_on_Player_attacked")


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	target = player.position
	time_moving = player.time_moving

	if (HP == 0):
		self.queue_free()

	var direction = target - self.position

	if (position.distance_to(target) > 10 && time_moving):
		var velocity = direction.normalized() * SPEED
		position += velocity * delta

	$Sprite.rotation = Vector2.UP.angle_to(direction)


func _on_Player_attacked(damage, distance):
	if position.distance_to(player.position) < distance:
		HP -= damage

A enemy/Enemy.tscn => enemy/Enemy.tscn +12 -0
@@ 0,0 1,12 @@
[gd_scene load_steps=3 format=2]

[ext_resource path="res://assets/player.png" type="Texture" id=1]
[ext_resource path="res://enemy/Enemy.gd" type="Script" id=2]

[node name="Enemy" type="Node2D"]
script = ExtResource( 2 )

[node name="Sprite" type="Sprite" parent="."]
modulate = Color( 0.870588, 0.133333, 0.133333, 0.372549 )
self_modulate = Color( 0.929412, 0.0823529, 0.0823529, 1 )
texture = ExtResource( 1 )

M project.godot => project.godot +39 -0
@@ 8,11 8,50 @@

config_version=4

_global_script_classes=[ {
"base": "Node2D",
"class": "Card",
"language": "GDScript",
"path": "res://Card.gd"
} ]
_global_script_class_icons={
"Card": ""
}

[application]

config/name="well-kard"
run/main_scene="res://Main.tscn"
config/icon="res://icon.png"

[input]

card_one={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":49,"unicode":0,"echo":false,"script":null)
 ]
}
card_two={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":50,"unicode":0,"echo":false,"script":null)
 ]
}
card_three={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":51,"unicode":0,"echo":false,"script":null)
 ]
}
card_four={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":52,"unicode":0,"echo":false,"script":null)
 ]
}
card_five={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":53,"unicode":0,"echo":false,"script":null)
 ]
}

[physics]

common/enable_pause_aware_picking=true