CoopPuzzle/scripts/GameField.gd

302 lines
10 KiB
GDScript

extends ColorRect
export var rows: int = 44
export var cols: int = 63
export var h_margin: int = 2
export var v_margin: int = 2
export var grid_color: Color = Color.black
var READY := false
const ByteArray2D = Common.ByteArray2D
const IntArray2D = Common.IntArray2D
onready var cell_numbers: ByteArray2D
const NUMBER_NONE := 255
onready var cell_colors: ByteArray2D
onready var corner_marks: IntArray2D
enum CornerMark {
L_RIGHT=1, L_DOWN=2, X_RIGHT=4, X_DOWN=8,
DARC_1=16, DARC_2=32, DARC_3=64, DARC_4=128,
SARC_1=256, SARC_2=512, SARC_3=1024, SARC_4=2048
}
func _ready():
# load_puzzle('res://slither_blank.txt')
load_puzzle('res://slither-202005192059.txt')
func draw_string_centered(font, position, string, color := Color.white):
draw_string(font, Vector2(position.x - font.get_string_size(string).x/2.0, position.y + font.get_ascent()), string, color)
const font := preload('res://dynamicfont.tres')
const COLORS = [null, Color.lightgreen, Color.lightyellow, Color.lightblue, Color.purple]
const N_COLORS = 5
var grid_0 := Vector2(0.0, 0.0)
var grid_space := Vector2(1.0, 1.0)
var h0 := 0.0
var v0 := 0.0
var h_space := 1.0
var v_space := 1.0
func update_grid_spacing() -> void:
h_space = rect_size.x / (h_margin * 2 + cols)
v_space = rect_size.y / (v_margin * 2 + rows)
if h_space > v_space:
h_space = v_space
else:
v_space = h_space
h0 = h_space * h_margin
v0 = v_space * v_margin
grid_0 = Vector2(h0, v0)
grid_space = Vector2(h_space, v_space)
func grid_corner(row, col) -> Vector2:
return (Vector2(col, row) * grid_space) + grid_0
func to_grid_space(position: Vector2) -> Vector2:
return (position - grid_0)/grid_space
func _draw() -> void:
if not READY:
return
update_grid_spacing()
font.set_size(int(ceil(v_space-5)))
# Colors
for row in rows:
for col in cols:
var color = COLORS[cell_colors.get_cell(row, col)]
if color != null:
draw_rect(Rect2(grid_corner(row, col), Vector2(h_space, v_space)), color, true)
# Lines
for row in rows:
for col in cols:
if corner_marks.get_flag(row, col, CornerMark.L_RIGHT):
draw_line(grid_corner(row, col), grid_corner(row, col+1), Color.blue)
elif corner_marks.get_flag(row, col, CornerMark.X_RIGHT):
draw_line(grid_corner(row, col+0.4), grid_corner(row, col+0.6), Color.red)
else:
draw_line(grid_corner(row, col), grid_corner(row, col+1), Color.white)
if corner_marks.get_flag(row, col, CornerMark.L_DOWN):
draw_line(grid_corner(row, col), grid_corner(row+1, col), Color.blue)
elif corner_marks.get_flag(row, col, CornerMark.X_DOWN):
draw_line(grid_corner(row+0.4, col), grid_corner(row+0.6, col), Color.red)
else:
draw_line(grid_corner(row, col), grid_corner(row+1, col), Color.white)
for col in cols:
if corner_marks.get_flag(rows, col, CornerMark.L_RIGHT):
draw_line(grid_corner(rows, col), grid_corner(rows, col+1), Color.blue)
elif corner_marks.get_flag(rows, col, CornerMark.X_RIGHT):
draw_line(grid_corner(rows, col+0.4), grid_corner(rows, col+0.6), Color.red)
else:
draw_line(grid_corner(rows, col), grid_corner(rows, col+1), Color.white)
for row in rows:
if corner_marks.get_flag(row, cols, CornerMark.L_DOWN):
draw_line(grid_corner(row, cols), grid_corner(row+1, cols), Color.blue)
elif corner_marks.get_flag(row, cols, CornerMark.X_DOWN):
draw_line(grid_corner(row+0.4, cols), grid_corner(row+0.6, cols), Color.red)
else:
draw_line(grid_corner(row, cols), grid_corner(row+1, cols), Color.white)
# Arc pencil marks
# Numbers
for row in rows:
for col in cols:
var num = cell_numbers.get_cell(row, col)
if num < 4:
draw_string_centered(font, grid_corner(row, col+0.5), str(num), Color.black)
# COORDS
for row in rows:
draw_string_centered(font, Vector2(h0/2, v0+row*v_space), '%02d'%row, Color.black)
for col in cols:
draw_string_centered(font, Vector2(h0+(col+0.5)*h_space, v0/4), num2alpha(col), Color.black)
func num2alpha(num: int, uppercase:=false):
var c = ord('A' if uppercase else 'a')
if num >= 26:
return char(c+(num/26)-1) + char(c+(num%26))
else:
return char(c+num)
func _process(delta: float) -> void:
update()
const num_dict := {'.': NUMBER_NONE, '0': 0, '1': 1, '2': 2, '3': 3}
const colornum_dict := {'0': 0, '1': 1, '2': 2}
func load_puzzle(filename: String):
var file := File.new()
var error = file.open(filename, File.READ)
if file.get_line() != 'pzprv3.1':
return
if file.get_line() != 'slither':
return
rows = int(file.get_line())
cols = int(file.get_line())
cell_numbers = ByteArray2D.new(rows, cols)
cell_colors = ByteArray2D.new(rows, cols)
corner_marks = IntArray2D.new(rows+1, cols+1, 0)
var row = 0
var stage = 0
var until = [rows, rows, rows, rows+1, 0]
while not file.eof_reached():
if row >= until[stage]:
stage += 1
row = 0
match stage:
0: # Puzzle definition numbers
var line = file.get_csv_line(' ')
var col = 0
for numstr in line:
if col < cols:
cell_numbers.set_cell(row, col, num_dict.get(numstr, NUMBER_NONE))
col += 1
1: # cell colors
var line = file.get_csv_line(' ')
var col = 0
for numstr in line:
if col < cols:
cell_colors.set_cell(row, col, colornum_dict.get(numstr, 0))
col += 1
2: # vert lines
var line = file.get_csv_line(' ')
var col = 0
for numstr in line:
if col < cols+1:
match int(numstr):
1:
corner_marks.set_flag(row, col, CornerMark.L_DOWN)
-1:
corner_marks.set_flag(row, col, CornerMark.X_DOWN)
col += 1
3: # horiz lines
var line = file.get_csv_line(' ')
var col = 0
for numstr in line:
if col < cols+1:
match int(numstr):
1:
corner_marks.set_flag(row, col, CornerMark.L_RIGHT)
-1:
corner_marks.set_flag(row, col, CornerMark.X_RIGHT)
col += 1
4:
break
row += 1
file.close()
READY = true
enum DragAction {TEST_LINE, DRAW_LINE, REMOVE_LINE, DRAW_X, REMOVE_X, DRAW_COLOR}
var drag_action = 0
var drag_color := 0
func _input(event: InputEvent) -> void:
if event is InputEventMouse:
var gridpos = to_grid_space(event.position)
var dx = fmod(gridpos.x, 1.0)
var dy = fmod(gridpos.y, 1.0)
var adx = 0.5 - abs(dx-0.5)
var ady = 0.5 - abs(dy-0.5)
var threshold = 0.2
var sel_col
var sel_row
if event is InputEventMouseButton and event.pressed:
if adx < threshold:
if ady < threshold:
drag_action = DragAction.TEST_LINE
else:
sel_row = int(gridpos.y)
sel_col = int(round(gridpos.x))
if corner_marks.get_flag(sel_row, sel_col, CornerMark.X_DOWN):
drag_action = DragAction.REMOVE_X
corner_marks.clear_flag(sel_row, sel_col, CornerMark.X_DOWN)
else:
drag_action = DragAction.DRAW_X
corner_marks.set_flag(sel_row, sel_col, CornerMark.X_DOWN)
else:
if ady < threshold:
sel_row = int(round(gridpos.y))
sel_col = int(gridpos.x)
if corner_marks.get_flag(sel_row, sel_col, CornerMark.X_RIGHT):
drag_action = DragAction.REMOVE_X
corner_marks.clear_flag(sel_row, sel_col, CornerMark.X_RIGHT)
else:
drag_action = DragAction.DRAW_X
corner_marks.set_flag(sel_row, sel_col, CornerMark.X_RIGHT)
else:
sel_row = int(gridpos.y)
sel_col = int(gridpos.x)
drag_action = DragAction.DRAW_COLOR
drag_color = posmod(cell_colors.get_cell(sel_row, sel_col) + (1 if event.button_index==BUTTON_LEFT else -1), N_COLORS)
cell_colors.set_cell(sel_row, sel_col, drag_color)
elif event is InputEventMouseMotion and event.button_mask:
if drag_action == DragAction.TEST_LINE:
if adx < threshold and ady > threshold:
drag_action = DragAction.REMOVE_LINE if corner_marks.get_flag(int(gridpos.y), int(round(gridpos.x)), CornerMark.L_DOWN) else DragAction.DRAW_LINE
elif adx > threshold and ady < threshold:
drag_action = DragAction.REMOVE_LINE if corner_marks.get_flag(int(round(gridpos.y)), int(gridpos.x), CornerMark.L_RIGHT) else DragAction.DRAW_LINE
match drag_action:
DragAction.DRAW_LINE:
if adx < threshold and ady > threshold:
set_line_down(int(gridpos.y), int(round(gridpos.x)))
elif adx > threshold and ady < threshold:
set_line_right(int(round(gridpos.y)), int(gridpos.x))
DragAction.REMOVE_LINE:
if adx < threshold and ady > threshold:
clear_line_down(int(gridpos.y), int(round(gridpos.x)))
elif adx > threshold and ady < threshold:
clear_line_right(int(round(gridpos.y)), int(gridpos.x))
DragAction.DRAW_X:
if adx < threshold and ady > threshold:
set_x_down(int(gridpos.y), int(round(gridpos.x)))
elif adx > threshold and ady < threshold:
set_x_right(int(round(gridpos.y)), int(gridpos.x))
DragAction.REMOVE_X:
if adx < threshold and ady > threshold:
clear_x_down(int(gridpos.y), int(round(gridpos.x)))
elif adx > threshold and ady < threshold:
clear_x_right(int(round(gridpos.y)), int(gridpos.x))
DragAction.DRAW_COLOR:
sel_row = int(gridpos.y)
sel_col = int(gridpos.x)
cell_colors.set_cell(sel_row, sel_col, drag_color)
var undo_stack = []
func set_line_right(row, col):
if !corner_marks.get_flag(row, col, CornerMark.L_RIGHT) and !corner_marks.get_flag(row, col, CornerMark.X_RIGHT):
corner_marks.set_flag(row, col, CornerMark.L_RIGHT)
# TODO: Add undo stack entry
func set_line_down(row, col):
if !corner_marks.get_flag(row, col, CornerMark.L_DOWN) and !corner_marks.get_flag(row, col, CornerMark.X_DOWN):
corner_marks.set_flag(row, col, CornerMark.L_DOWN)
func clear_line_right(row, col):
if corner_marks.get_flag(row, col, CornerMark.L_RIGHT):
corner_marks.clear_flag(row, col, CornerMark.L_RIGHT)
func clear_line_down(row, col):
if corner_marks.get_flag(row, col, CornerMark.L_DOWN):
corner_marks.clear_flag(row, col, CornerMark.L_DOWN)
func set_x_right(row, col):
if !corner_marks.get_flag(row, col, CornerMark.L_RIGHT) and !corner_marks.get_flag(row, col, CornerMark.X_RIGHT):
corner_marks.set_flag(row, col, CornerMark.X_RIGHT)
func set_x_down(row, col):
if !corner_marks.get_flag(row, col, CornerMark.L_DOWN) and !corner_marks.get_flag(row, col, CornerMark.X_DOWN):
corner_marks.set_flag(row, col, CornerMark.X_DOWN)
func clear_x_right(row, col):
if corner_marks.get_flag(row, col, CornerMark.X_RIGHT):
corner_marks.clear_flag(row, col, CornerMark.X_RIGHT)
func clear_x_down(row, col):
if corner_marks.get_flag(row, col, CornerMark.X_DOWN):
corner_marks.clear_flag(row, col, CornerMark.X_DOWN)