diff options
| -rw-r--r-- | Objects/BuilderObject.gd | 8 | ||||
| -rw-r--r-- | Start.tscn | 6 | ||||
| -rw-r--r-- | Tray.gd | 45 | ||||
| -rw-r--r-- | Tray.tscn | 15 | ||||
| -rw-r--r-- | app.html | 217 | ||||
| -rw-r--r-- | export_presets.cfg | 2 | ||||
| -rw-r--r-- | project.godot | 4 | 
7 files changed, 287 insertions, 10 deletions
| diff --git a/Objects/BuilderObject.gd b/Objects/BuilderObject.gd index 91465bc..fde2660 100644 --- a/Objects/BuilderObject.gd +++ b/Objects/BuilderObject.gd @@ -30,7 +30,7 @@ func _process(_delta):  func _on_area_2d_input_event(_viewport, event: InputEvent, _shape_idx):  	if event.is_action_pressed("drag_start"):  		drag_start() -	if event.is_action_released("drag_start"): +	if is_dragging and event.is_action_released("drag_start"):  		drag_end() @@ -79,6 +79,6 @@ func _on_area_2d_area_entered(_area):  func _on_area_2d_area_exited(_area): -	#if $Area2D.get_overlapping_areas().size() == 0: -	is_colliding = false -	modulate = Color("fff") +	if $Area2D.get_overlapping_areas().size() == 0: +		is_colliding = false +		modulate = Color("fff") @@ -1,7 +1,6 @@ -[gd_scene load_steps=5 format=3 uid="uid://bdyngwtm3rowr"] +[gd_scene load_steps=4 format=3 uid="uid://bdyngwtm3rowr"]  [ext_resource type="Script" path="res://Start.gd" id="1_k0odd"] -[ext_resource type="PackedScene" uid="uid://p0ay1mp7v772" path="res://Objects/BuilderObject.tscn" id="3_skats"]  [ext_resource type="PackedScene" uid="uid://3vuctgbcjqi7" path="res://Tray.tscn" id="3_u4wpj"]  [ext_resource type="Texture2D" uid="uid://djasmoqj87h1r" path="res://icon.svg" id="4_kyq1e"] @@ -39,9 +38,6 @@ texture = ExtResource("4_kyq1e")  [node name="DropTarget" type="Node2D" parent="Map"]  unique_name_in_owner = true -[node name="BuilderObject" parent="Map/DropTarget" instance=ExtResource("3_skats")] -position = Vector2(155, 109) -  [node name="Viewport" type="Node2D" parent="."]  unique_name_in_owner = true @@ -4,9 +4,15 @@ extends Control  var TrayItemScene = preload("res://TrayItem.tscn")  var is_open = true +var _on_data_loaded_callback = null  func _ready(): +	if OS.get_name() == "Web": +		_on_data_loaded_callback = JavaScriptBridge.create_callback(load_save) +		var callbacks = JavaScriptBridge.get_interface("godotCallbacks") +		callbacks.dataLoaded = _on_data_loaded_callback +	  	for i in range(5):  		add_item() @@ -55,3 +61,42 @@ func _on_button_pressed():  		close()  	else:  		open() + + +func _on_save_pressed(): +	if OS.get_name() == "Web": +		var to_save = [] +		var objects = get_tree().current_scene.get_node("%DropTarget").get_children() +		for obj in objects: +			to_save.append({ +				"position": { +					"x": obj.position.x, +					"y": obj.position.y, +				}, +				"rotation": obj.rotation, +				"object": "BuilderObject", +			}) +		 +		JavaScriptBridge.download_buffer( +			JSON.stringify(to_save).to_utf8_buffer(), +			"CityBuilder-save.json", +			"application/json" +		) + + +func _on_load_pressed(): +	if OS.get_name() == "Web": +		JavaScriptBridge.eval("openLoadDialog()") + + +func load_save(data: Array): +	var current_objects = get_tree().current_scene.get_node("%DropTarget").get_children() +	for obj in current_objects: +		obj.queue_free() +	 +	var objects = JSON.parse_string(data[0]) +	for obj in objects: +		var scene = load("res://Objects/" + obj.object + ".tscn").instantiate() +		scene.position = Vector2(obj.position.x, obj.position.y) +		scene.rotation = obj.rotation +		get_tree().current_scene.get_node("%DropTarget").add_child(scene) @@ -38,6 +38,19 @@ layout_mode = 2  size_flags_horizontal = 3  size_flags_vertical = 3 +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Save" type="Button" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Save" + +[node name="Load" type="Button" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Load" +  [node name="Control" type="Control" parent="."]  layout_mode = 2  size_flags_horizontal = 0 @@ -60,4 +73,6 @@ collision_mask = 3  [node name="CollisionShape2D" type="CollisionShape2D" parent="TrayCollision"]  [connection signal="resized" from="." to="." method="_on_resized"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/Save" to="." method="_on_save_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/Load" to="." method="_on_load_pressed"]  [connection signal="pressed" from="Control/Button" to="." method="_on_button_pressed"] diff --git a/app.html b/app.html new file mode 100644 index 0000000..078d27d --- /dev/null +++ b/app.html @@ -0,0 +1,217 @@ +<!DOCTYPE html> +<html lang="en"> +  <head> +    <meta charset="utf-8"> +    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"> +    <title>$GODOT_PROJECT_NAME</title> +    <style> +    html, body, #canvas { +      margin: 0; +      padding: 0; +      border: 0; +    } + +    body { +      color: white; +      background-color: black; +      overflow: hidden; +      touch-action: none; +    } + +    #canvas { +      display: block; +    } + +    #canvas:focus { +      outline: none; +    } + +    #status, #status-splash, #status-progress { +      position: absolute; +      left: 0; +      right: 0; +    } + +    #status, #status-splash { +      top: 0; +      bottom: 0; +    } + +    #status { +      background-color: #242424; +      display: flex; +      flex-direction: column; +      justify-content: center; +      align-items: center; +      visibility: hidden; +    } + +    #status-splash { +      max-height: 100%; +      max-width: 100%; +      margin: auto; +    } + +    #status-progress, #status-notice { +      display: none; +    } + +    #status-progress { +      bottom: 10%; +      width: 50%; +      margin: 0 auto; +    } + +    #status-notice { +      background-color: #5b3943; +      border-radius: 0.5rem; +      border: 1px solid #9b3943; +      color: #e0e0e0; +      font-family: 'Noto Sans', 'Droid Sans', Arial, sans-serif; +      line-height: 1.3; +      margin: 0 2rem; +      overflow: hidden; +      padding: 1rem; +      text-align: center; +      z-index: 1; +    } +    </style> +    $GODOT_HEAD_INCLUDE +    <script> +    var godotCallbacks = { +      dataLoaded: null +    } + +    function openLoadDialog() { +      var input = document.createElement('input'); +      input.setAttribute('type', 'file'); +      input.setAttribute('accept', '.json'); +      input.click(); + +      input.addEventListener('change', (event) => { +        var file = event.target.files[0]; +        var reader = new FileReader(); + +        reader.readAsText(file); + +        reader.onloadend = () => { +          if (godotCallbacks.dataLoaded) { +            godotCallbacks.dataLoaded(reader.result); +          } +        } +      }); +    } +    </script> +  </head> +  <body> +    <canvas id="canvas"> +      Your browser does not support the canvas tag. +    </canvas> + +    <noscript> +      Your browser does not support JavaScript. +    </noscript> + +    <div id="status"> +      <img id="status-splash" src="$GODOT_SPLASH" alt=""> +      <progress id="status-progress"></progress> +      <div id="status-notice"></div> +    </div> + +    <script src="$GODOT_URL"></script> +    <script> +    const GODOT_CONFIG = $GODOT_CONFIG; +    // const GODOT_THREADS_ENABLED = $GODOT_THREADS_ENABLED; +    const GODOT_THREADS_ENABLED = 'yes'; +    const engine = new Engine(GODOT_CONFIG); + +    (function () { +      const statusOverlay = document.getElementById('status'); +      const statusProgress = document.getElementById('status-progress'); +      const statusNotice = document.getElementById('status-notice'); + +      let initializing = true; +      let statusMode = ''; + +      function setStatusMode(mode) { +        if (statusMode === mode || !initializing) { +          return; +        } +        if (mode === 'hidden') { +          statusOverlay.remove(); +          initializing = false; +          return; +        } +        statusOverlay.style.visibility = 'visible'; +        statusProgress.style.display = mode === 'progress' ? 'block' : 'none'; +        statusNotice.style.display = mode === 'notice' ? 'block' : 'none'; +        statusMode = mode; +      } + +      function setStatusNotice(text) { +        while (statusNotice.lastChild) { +          statusNotice.removeChild(statusNotice.lastChild); +        } +        const lines = text.split('\n'); +        lines.forEach((line) => { +          statusNotice.appendChild(document.createTextNode(line)); +          statusNotice.appendChild(document.createElement('br')); +        }); +      } + +      function displayFailureNotice(err) { +        const msg = err.message || err; +        console.error(msg); +        setStatusNotice(msg); +        setStatusMode('notice'); +        initializing = false; +      } + +      const missing = Engine.getMissingFeatures({ +        threads: GODOT_THREADS_ENABLED, +      }); + +      if (missing.length !== 0) { +        if (GODOT_CONFIG['serviceWorker'] && GODOT_CONFIG['ensureCrossOriginIsolationHeaders'] && 'serviceWorker' in navigator) { +          // There's a chance that installing the service worker would fix the issue +          Promise.race([ +            navigator.serviceWorker.getRegistration().then((registration) => { +              if (registration != null) { +                return Promise.reject(new Error('Service worker already exists.')); +              } +              return registration; +            }).then(() => engine.installServiceWorker()), +            // For some reason, `getRegistration()` can stall +            new Promise((resolve) => { +              setTimeout(() => resolve(), 2000); +            }), +          ]).catch((err) => { +              console.error('Error while registering service worker:', err); +            }).then(() => { +              window.location.reload(); +            }); +        } else { +          // Display the message as usual +          const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n'; +          displayFailureNotice(missingMsg + missing.join('\n')); +        } +      } else { +        setStatusMode('progress'); +        engine.startGame({ +          'onProgress': function (current, total) { +            if (current > 0 && total > 0) { +              statusProgress.value = current; +              statusProgress.max = total; +            } else { +              statusProgress.removeAttribute('value'); +              statusProgress.removeAttribute('max'); +            } +          }, +        }).then(() => { +            setStatusMode('hidden'); +          }, displayFailureNotice); +      } +    }()); +    </script> +  </body> +</html> diff --git a/export_presets.cfg b/export_presets.cfg index 542cb18..4deecb9 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -22,7 +22,7 @@ variant/extensions_support=false  vram_texture_compression/for_desktop=true  vram_texture_compression/for_mobile=false  html/export_icon=true -html/custom_html_shell="" +html/custom_html_shell="res://app.html"  html/head_include=""  html/canvas_resize_policy=2  html/focus_canvas_on_start=true diff --git a/project.godot b/project.godot index 2f94abc..a703fc6 100644 --- a/project.godot +++ b/project.godot @@ -15,6 +15,10 @@ run/main_scene="res://Start.tscn"  config/features=PackedStringArray("4.2", "GL Compatibility")  config/icon="res://icon.svg" +[audio] + +driver/mix_rate.web=44100 +  [input]  drag_start={ | 
