Mineshafts-prototype2/engine/core.gd

793 lines
22 KiB
GDScript

extends Node
#woody
#cracky
#elastic
#crumbly
var error = false
var version = 0.1
var save = {player={}, objects={}, mods={},map={}}
var save_timer = 0
var chunk #noderef functions
var main #noderef functions
var WaEditor #noderef functions
var ui #noderef functions
var Loader #noderef functions
var chunks = {}
var chunk_size=8#16
var player
var objects = {}
var viewport_items = {}
var game_paused = true
var world_name = ""
var world = {name="earth",mapgen="super_flatland",meta={}}
var game
var map = {}
var meta = {}
var settings = {
update_chunk_rules=[Vector3(-1,0,0),Vector3(1,0,0),Vector3(0,0,-1),Vector3(0,0,1),Vector3(0,-1,0),Vector3(0,1,0)],
ganerate_chunk_range = 4,
max_update_nodes = 512,
save_timeout = 5,
unload_chunk_distance = 100,
item_drop_timeout = 300,
}
var default_node_id = 7
var current_mapgen # funcref
var register = {
items = {
max_count = 1,
"wieldhand":{name="wieldhand",
type = "item",
max_count = 1,
inv_image=load("res://game/textures/wieldhand.png"),
item_capabilities = {
punch_interval = 1,
damage = 1,
groups = {crumbly=1},
},
},
"default:stone_pick":{name="default:stone_pick",
max_count = 1,
type = "item",
inv_image=load("res://game/textures/default_pick_stone.png"),
item_capabilities = {
punch_interval = 1,
damage = 1,
durability = 100,
groups = {cracky=1},
},
},
"default:stone_hoe":{name="default:stone_hoe",
max_count = 1,
type = "item",
inv_image=load("res://game/textures/default_hoe_stone.png"),
item_capabilities = {
punch_interval = 1,
damage = 1,
durability = 100,
groups = {crumbly=1},
}
},
"default:stone_shovel":{name="default:stone_shovel",
max_count = 1,
type = "item",
inv_image=load("res://game/textures/default_shovel_stone.png"),
item_capabilities = {
punch_interval = 1,
damage = 1,
durability = 100,
groups = {crumbly=2},
}
},
"default:axe_stone":{name="default:axe_stone",
max_count = 1,
type = "item",
inv_image=load("res://game/textures/default_axe_stone.png"),
item_capabilities = {
punch_interval = 1,
damage = 1,
durability = 100,
groups = {woody=1},
}
},
"default:stick":{name="default:stick",
max_count = 100,
type = "item",
inv_image=load("res://game/textures/default_stick.png"),
groups = {"stick":1},
},
},
nodes = {
none={id=0,name="none",drawtype="none",tiles=[],type="node",groups={},max_count=100,},
air={id=1,name="air",drawtype="none",tiles=[],collidable=false,solid_surface=false,type="node",groups={},max_count=100,},
default={id=2,name="default",drawtype="default",tiles=[load("res://game/textures/default.png")],type="node",groups={cracky=1},max_count=100,},
},
id = {0:"none",1:"air",2:"default"}
}
var default_node = {
texture = load("res://game/textures/default.png"),
uv = [Vector2(0,0),Vector2(0,1),Vector2(1,1),Vector2(1,0)],
dir = [Vector3(0,1,0),Vector3(0,-1,0),Vector3(1,0,0),Vector3(-1,0,0),Vector3(0,0,-1),Vector3(0,0,1)],
faces = [
[Vector3(1,0,0),Vector3(1,0,1),Vector3(0,0,1),Vector3(0,0,0)], #up y+
[Vector3(0,-1,0),Vector3(0,-1,1),Vector3(1,-1,1),Vector3(1,-1,0)], #down y-
[Vector3(1,0,0),Vector3(1,-1,0),Vector3(1,-1,1),Vector3(1,0,1)], #north x+
[Vector3(0,0,1),Vector3(0,-1,1),Vector3(0,-1,0),Vector3(0,0,0)], #south x-
[Vector3(0,0,0),Vector3(0,-1,0),Vector3(1,-1,0),Vector3(1,0,0)], #east z+
[Vector3(1,0,1),Vector3(1,-1,1),Vector3(0,-1,1),Vector3(0,0,1)], #west z-
]
}
func _init():
set_process(false)
func register_node(def):
var id = register.id.size()
if def.get("tiles") != null:
for i in range(0,def.tiles.size()):
var t = def.tiles[i]
if t is String:
var l = load(t)
if l is StreamTexture:
def.tiles[i] = l
def.id = id
def.type = "node"
def.drop = def.get("drops")
def.drawtype = "normal" if def.get("drawtype") == null else def.drawtype
def.groups = {} if def.get("groups") == null else def.groups
def.max_count = 100 if def.get("max_count") == null else def.max_count
def.replaceable = false if def.get("replaceable") == null else def.replaceable
def.collidable = true if def.get("collidable") == null else def.collidable
def.transparent = false if def.get("transparent") == null else def.transparent
def.solid_surface = false if def.get("solid_surface") == null else def.solid_surface
def.touchable = false if def.get("touchable") == null else def.touchable
def.activatable = false if def.get("activatable") == null else def.activatable
def.tiles = [] if def.get("tiles") == null else def.tiles
#on_activate func ref
register.id[id] = def.name
register.nodes[def.name] = def
func world_main(change=false):
Loader.start(100,{0:"World",1:"Saves",2:"Mods",3:"Preparing the map"})
if change:
save_data()
world_quit(false,false)
world.meta = {}
game = Spatial.new()
main.add_child(core.game)
var w = load("res://engine/chunk/chunk.tscn").instance()
game.add_child(w)
Loader.setprogress(1)
load_data()
load_player()
Loader.setprogress(2)
WaEngine.scan()
if core.error:
Loader.end()
return
WaEngine.init()
Loader.setprogress(3)
current_mapgen = funcref(mapgen,world.mapgen)
set_process(true)
mapgen.set_process(true)
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
ui.visible = true
game_paused = false
get_tree().paused = false
yield(get_tree().create_timer(1),"timeout")
var l = chunk.chunks_in_progress.size()
var s = l
Loader.start(l,{0:"loading map"})
while l > 0:
l = chunk.chunks_in_progress.size()
Loader.setprogress(s-l)
yield(get_tree().create_timer(0),"timeout")
func world_quit(quit=false,to_mainmanu=true):
core.save_data()
if quit:
get_tree().quit()
return
ui.visible = false
WaEditor.visible = false
set_process(false)
mapgen.set_process(false)
chunk.queue_free()
game.queue_free()
game = null
player.object.queue_free()
player = null
map.clear()
meta.clear()
chunks.clear()
objects.clear()
WaEngine.nodes.clear()
chunk.chunks_in_progress.clear()
if to_mainmanu:
var manu = load("res://engine/MainManu/MainManu.tscn").instance()
main.add_child(manu)
func game_pause():
game_paused = game_paused == false
get_tree().paused = game_paused
if game_paused:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
ui.get_node("manu").visible = true
else:
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
ui.get_node("manu").visible = false
func add_object(ob,type="",url=null):
var id = 0
while objects.get(id) != null:
id += 1
objects[id] = {
name = str(id),
id = id,
type = type,
inventory = {main=new_inventory(1)},
object = ob,
meta = {},
url = url,
}
return objects[id]
func load_objects():
for i in save.objects:
var s = save.objects[i]
objects[s.id] = {}
var o = objects[s.id]
o.id = s.id
o.name = s.name
o.type = s.type
o.inventory = s.inventory
o.url = s.url
o.meta = s.meta
o.object = load(o.url).instance()
o.object.transform.origin = s.pos
o.object.rotation = s.rotation
o.object.scale = s.scale
inv_remove_invalid_items(s.inventory)
game.add_child(o.object)
if o.object.has_method("on_load"):
o.object.on_load(o)
func load_player(name:String="player"):
player = {
player=true,
name = name,
id = 0,
right_item = null,
left_item = null,
inventory = {
main=new_inventory(32),
craft=new_inventory(9),
craftoutput=new_inventory(1)
},
slot_index = 0,
hotbar_count = 8,
right_slot = 4,
left_slot = 0,
object = load("res://engine/player/player.tscn").instance(),
meta = {}
}
var s = save.player
player.object.player = player
if s != null and s.size() > 0:
player.object.transform.origin = s.pos
player.object.rotation = s.rotation
for i in s:
player[i] = s[i]
inv_remove_invalid_items(player.inventory)
else:
player.object.transform.origin = Vector3(0,2,0)
player.inventory={
main=core.new_inventory(32,[
core.itemstack("default:chest",{count=10}),
core.itemstack("default:stone_pick"),
]),
craft=core.new_inventory(9),
craftoutput=core.new_inventory(1)
}
game.add_child(player.object)
func save_player_objects():
if player != null:
var sp = {}
sp.slot_index = player.slot_index
sp.right_slot = player.right_slot
sp.left_slot = player.left_slot
sp.pos = player.object.transform.origin
sp.inventory = player.inventory
sp.rotation = player.object.rotation
sp.meta = player.meta
save.player = sp
var ob2s = {}
for i in objects:
var o = objects[i]
if o.get("object") != null and o.url != null and is_instance_valid(o.object):
var s = {}
s.id = o.id
s.name = o.name
s.type = o.type
s.inventory = o.inventory
s.url = o.url
s.meta = o.meta
s.pos = o.object.global_transform.origin
s.rotation = o.object.rotation
s.scale = o.object.scale
ob2s[i] = s
else:
objects.erase(i)
save.objects = ob2s
func load_file(path):
var file = File.new()
if not file.file_exists(path):
return
file.open(path,file.READ)
var l = file.get_var()
file.close()
return l
func save_file(path,s):
var D = Directory.new()
var dir = path.get_base_dir().get_file()
if D.dir_exists(str("user://",dir)) == false:
D.make_dir(str("user://",dir))
var file = File.new()
file.open(str(path),file.WRITE_READ)
file.store_var(s)
file.close()
func load_data():
var s = load_file(str("user://worlds/",world_name,".mworld"))
save.mods = s.mods
save.player = s.player
if s.get(world.name):
world = s[world.name].world
save.objects = s[world.name].objects
save.map = s[world.name].map.duplicate()
meta = s[world.name].meta.duplicate()
load_objects()
else:
world = world
save.objects = {}
save.map = {}
meta = {}
return true
func save_data():
if world_name == "":
return
save_player_objects()
var s = load_file(str("user://worlds/",world_name,".mworld"))
if s == null:
s = {}
s.player = save.player
s.mods = save.mods
s[world.name] = {
world=world,
objects=save.objects,
meta=meta,
map=save.map,
}
save_file(str("user://worlds/",world_name,".mworld"),s)
func delete_data(wn=world_name):
var d = Directory.new()
d.remove(str("user://worlds/",wn,".mworld"))
func _process(delta):
save_timer += delta
if save_timer > settings.save_timeout:
save_timer = 0
save_data()
func get_pointed_pos():
var pos = player.object.get_translation()
var aim = player.object.get_node("head/Camera").get_global_transform().basis
var hpos = player.object.get_node("head").get_translation()
#var lpos = (pos + hpos+aim.z).round()
for i in range(-1,-500,-1):
var p = (pos + hpos+(aim.z*Vector3(i*0.01,i*0.01,i*0.01)))#.round()
var rp = p.round()
var id = map.get(rp)
var reg = get_node_reg(id)
if reg != null and reg.get("drawtype") != "none" and reg.get("collidable") != false:
if (p.x >= rp.x-0.5 and p.x <= rp.x+0.5) and (p.y >= rp.y-0.5 and p.y <= rp.y+0.5) and (p.z >= rp.z-0.5 and p.z <= rp.z+0.5):
return {inside=rp,outside=p}
#lpos = rp
return {inside=null,outside=null}
func place_node(node,inside:bool=false):
var pos = player.object.get_translation()
var aim = player.object.get_node("head/Camera").get_global_transform().basis
var hpos = player.object.get_node("head").get_translation()
var lpos = (pos + hpos+aim.z).round()
for i in range(-1,-500,-1):
var p = (pos + hpos+(aim.z*Vector3(i*0.01,i*0.01,i*0.01)))#.round()
var rp = p.round()
var id = map.get(rp)
var reg = get_node_reg(id)
if reg != null and reg.get("drawtype") != "none" and reg.get("collidable") != false and chunk.get_chunk_at_pos(rp):
if (p.x >= rp.x-0.5 and p.x <= rp.x+0.5) and (p.y >= rp.y-0.5 and p.y <= rp.y+0.5) and (p.z >= rp.z-0.5 and p.z <= rp.z+0.5):
if get_node_reg(node).get("collidable") == false or (pos-hpos).distance_to(p) > 0.9 and (pos+hpos).distance_to(p) > 1:
var pp = rp if inside == true or reg.get("replaceable") == true else lpos
set_node({name=node,pos=pp})
return true
return false
lpos = rp
return false
func get_item_reg(v):
var it = register.items.get(v)
if it:
return it
elif v is Vector3:
v = v.round()
v = map.get(v)
elif v is String:
return register.nodes.get(v)
var n = register.id.get(v)
return register.nodes.get(n)
func get_node_reg(v):
if v is Vector3:
v = v.round()
v = map.get(v)
elif v is String:
return register.nodes.get(v)
var n = register.id.get(v)
return register.nodes.get(n)
func set_node(def:Dictionary):# set node
assert(typeof(def.get("pos")) == TYPE_VECTOR3 and typeof(def.get("name")) == TYPE_STRING,"ERROR: set_node: def.pos & def.name required!")
var n = register.nodes.get(def.name)
assert(n != null,str('ERROR: set_node: "',def.name,'"', " doesn't exists"))
var rpos = def.pos.round()
var reg = get_node_reg(def.name)
node_removed(map[rpos],rpos)
map[rpos] = reg.id
save.map[rpos] = reg.id
var cid = chunk.pos_to_chunkid(rpos)
for r in settings.update_chunk_rules.size():
var near_chunk = rpos+settings.update_chunk_rules[r]
if chunk.pos_to_chunkid(near_chunk) != cid:
chunk.update(near_chunk)
chunk.update(rpos)
func inset_map_node_id(id,pos):
map[pos] = id
func item2mesh(item, priority_render:bool = false):
var reg = get_item_reg(item)
var mesh = Mesh.new()
var st = SurfaceTool.new()
if reg.type == "node":
var tiles = reg.tiles
var tile
if reg.drawtype == "none":
return mesh
for f in default_node.faces.size()+1:
st.begin(Mesh.PRIMITIVE_TRIANGLE_FAN)
var mat = SpatialMaterial.new()
if tiles.size() >= f:
tile = tiles[f-1]
mat.flags_transparent = (reg.get("transparent") == true)
mat.albedo_texture = tile
st.set_material(mat)
for v in range(0,4):
st.add_uv(default_node.uv[v])
st.add_vertex(default_node.faces[f-1][v]-Vector3(0.5,-0.5,0.5))#-Vector3(0,0,0.5)
st.commit(mesh)
st.clear()
else:
var texture = reg.inv_image
var faces = default_node.faces
var img = texture.get_data()
var s = img.get_size()
img.decompress()
img.flip_y()
img.flip_x()
img.lock()
var size = Vector3(0.075,0.075,0.075)
for y in s.y:
for x in s.x:
var c = img.get_pixel(x,y)
if c.a != 0:
var f = [4,5]
var mat = SpatialMaterial.new()
mat.albedo_color = c
mat.flags_no_depth_test = priority_render
if y == s.y-1 or img.get_pixel(x,y+1).a == 0:
f.push_back(0)
if y == 0 or img.get_pixel(x,y-1).a == 0:
f.push_back(1)
if x == s.x-1 or img.get_pixel(x+1,y).a == 0:
f.push_back(2)
if x == 0 or img.get_pixel(x-1,y).a == 0:
f.push_back(3)
for n in f:
st.begin(Mesh.PRIMITIVE_TRIANGLE_FAN)
st.set_material(mat)
for v in 4:
st.add_vertex((faces[n][v]+Vector3(x-7,y-7,0))*size)
st.commit(mesh)
st.clear()
img.unlock()
return mesh
func new_inventory(size:int = 32,items:Array=[]):
items.resize(size)
return items
func itemstack(item:String,def:Dictionary={}):
var reg = get_item_reg(item)
if reg == null:
return null
var durability = 0 if reg.get("item_capabilities") == null or reg.item_capabilities.get("durability") == null else reg.item_capabilities.durability
return {
name = item if def.get("item") == null else def.get("item"),
count = def.get("count") if def.get("count") != null and def.get("count") <= reg.max_count else reg.max_count,
durability = durability if def.get("durability") == null else def.get("durability"),
meta = {} if def.get("meta") == null else def.get("meta"),
}
func item_groups(item):
var it = register.items.get(item)
var nu = register.nodes.get(item)
if it:
var cap = it.get("item_capabilities")
if cap != null:
return cap.groups
elif nu:
return nu.groups
func stack_2invitem(stack):
var t = TextureRect.new()
var reg = get_item_reg(stack.name)
t.texture = item3Dimg(stack.name)
#t.texture = reg.get("inv_image") if reg.type == "item" else reg.tiles[0]
t.expand = true
t.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
t.rect_size = Vector2(100,100)
t.rect_clip_content = true
t.name = "item"
var d = Node2D.new()
d.add_child(t)
var c = reg.get("item_capabilities")
if c != null and c.get("durability") != null:
var bar1 = ColorRect.new()
bar1.rect_size = Vector2(100,3)
bar1.rect_position = Vector2(0,96)
bar1.color = Color(255,0,0)
t.add_child(bar1)
var bar2 = ColorRect.new()
var d1 = float(stack.durability)/float(c.durability)
bar2.rect_size = Vector2(100*d1,3)
bar2.color = Color(0,255,0)
bar1.add_child(bar2)
else:
var p = Label.new()
p.text = str(stack.count)
p.rect_size = Vector2(50,5)
p.rect_position = Vector2(0,70)
p.rect_scale = Vector2(2,2)
p.align = HALIGN_RIGHT
p.name = "text"
t.add_child(p)
return d
func setMeta(pos:Vector3,label,set=null):
if label == null:
meta.erase(pos)
return
meta[pos] = {} if meta.get(pos) == null else meta[pos]
var lab = meta[pos].get(label)
meta[pos][label] = {} if lab == null else meta[pos][label]
if lab == null:
meta[pos].erase(pos)
else:
meta[pos][label] = set
func getMeta(pos:Vector3,label=null):
if meta.get(pos) == null or label == null:
return
return meta[pos].get(label)
func existsMeta(pos):
return meta.get(pos) != null
func create_node_inventory(pos:Vector3,name,size:int=32,add_to_stack=null):
meta[pos] = {} if meta.get(pos) == null else meta[pos]
meta[pos].inventory = {} if meta[pos].get("inventory") == null else meta[pos].inventory
meta[pos].inventory[name] = new_inventory(size,add_to_stack)
func get_inventory(ref,name):
var inv
if ref is Vector3:
if meta.get(ref):
if meta[ref].get("inventory") != null and meta[ref].inventory.get(name):
inv = meta[ref].inventory[name]
elif ref.get("player"):
inv = ref.inventory[name]
elif objects.get(ref.id):
inv = ref.inventory[name]
return inv
func get_inventory_stack(ref,name,i):
var inv = get_inventory(ref,name)
var stack = inv[i]
if stack != null:
var reg = get_item_reg(stack.name)
if reg == null:
inv[i] = null
else:
return stack
func add_to_inventory(ref,name,stack):
var inv
var reg = get_item_reg(stack.name)
var stack2 = stack.duplicate()
if reg == null:
return stack2
elif ref is Vector3:
if meta.get(ref):
if meta[ref].get("inventory") != null and meta[ref].inventory.get(name):
inv = meta[ref].inventory[name]
elif player.get(ref.name):
inv = ref.inventory[name]
elif objects.get(ref.id):
inv = ref.inventory[name]
else:
return stack2
var c = stack.count
var m = reg.max_count
for i in inv.size():
var slot = inv[i]
if slot != null and slot.name == stack.name:
var can_add = m - slot.count
if can_add <= c:
slot.count += can_add
c -= can_add
else:
slot.count += c
c = 0
stack2.count = c
if c <= 0:
return stack2
for i in inv.size():
var slot = inv[i]
if slot == null:
if m <= c:
inv[i] = itemstack(reg.name,{count=m})
c -= m
else:
inv[i] = itemstack(reg.name,{count=c})
c = 0
stack2.count = c
if c <= 0:
return stack2
return stack2
func get_drop(name_pos_id):
var reg = get_item_reg(name_pos_id)
var drop = reg.get("drop")
var drops = {}
var a = {}
if drop == null:
drops[reg.name] = 1
elif drop is String:
drops[drop] = 1
elif drop is Dictionary:
for i in drop:
var d = drop[i]
if d.get("chance") == null or round(rand_range(0,d.chance)) == d.chance:
var count = 1 if d.get("count") == null else d.count
drops[i] = count if drops.get(i) == null else drops[i]+count
if d.get("additional") == false:
drops = {}
drops[i] = count
break
for i in drops:
a[i]=itemstack(i,{count=drops[i]})
return a
func item3Dimg(item):
var texture = viewport_items.get(item)
if texture == null:
var tex = load("res://engine/item_to_texture/item_to_texture.tscn").instance()
add_child(tex)
texture = tex.texture(item)
viewport_items[item] = texture
return texture
func spawn_drop_item(pos,stack,droper=null):
var d = load("res://game/item_drop/item_drop.tscn").instance()
var ob = add_object(d,"item_drop",d.filename)
if d.has_method("on_spawn"):
d.on_spawn(ob)
d.spawn(pos,stack,droper,ob)
add_child(d)
return d
func inv_remove_invalid_items(inv):
for list in inv:
var i = 0
for stack in inv[list]:
if stack != null and get_item_reg(stack.name) == null:
inv[list][i] = null
i += 1
func Error(msg):
error = true
world_quit()
main.get_node("exitmsg/center/text").text = msg
main.move_child(main.get_node("exitmsg"),main.get_child_count())
main.get_node("exitmsg").visible = true
yield(get_tree().create_timer(0.01),"timeout")
error = false
func option_flash(o,text="",color=Color(1,0,0,0.8),blacktext=false):
var b = Node2D.new()
b.z_index = 1000
add_child(b)
var u = ColorRect.new()
u.rect_size = o.rect_size
u.rect_position = o.rect_global_position
u.color = color
b.add_child(u)
var l = Label.new()
l.text = text
l.align = Label.ALIGN_CENTER
l.valign = Label.VALIGN_CENTER
l.rect_size = o.rect_size
u.add_child(l)
u.rect_size.x = l.rect_size.x
l.add_color_override("font_color",Color(0,0,0) if blacktext else Color(1,1,1))
yield(get_tree().create_timer(1),"timeout")
u.free()
func node_set(id,pos):
var reg = get_node_reg(id)
if reg.get("touchable"):
var c = chunk.get_chunk_at_pos(pos)
var a = Area.new()
var col = CollisionShape.new()
col.shape = BoxShape.new()
col.scale = Vector3(0.51,0.51,0.51)
c.add_child(a)
a.add_child(col)
a.name = str(var2str(pos),"touchable")
a.global_transform.origin = pos
a.connect("body_entered",self,"node_touch",[id,pos,true])
a.connect("body_exited",self,"node_touch",[id,pos,false])
func node_removed(id,pos):
var reg = get_node_reg(id)
if reg.get("touchable"):
var c = chunk.get_chunk_at_pos(pos)
var n = str(var2str(pos),"touchable")
if c.has_node(n):
c.get_node(n).queue_free()
func node_touch(body,id,pos,touch):
if not body is StaticBody:
var reg = get_node_reg(id)
for i in WaEngine.nodes:
var ob = WaEngine.nodes[i]
if ob.name == "register_node" and ob.options.touchable and ob.options.name == reg.name:
if touch:
WaEngine.next_connection(ob,"on_touch",body)
else:
WaEngine.next_connection(ob,"on_untouch",body)
yield(get_tree().create_timer(0.1),"timeout")
WaEngine.next_connection(ob,"pos",pos)