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)