# Hurry Curry! - a game about cooking # Copyright 2024 nokoe # Copyright 2024 metamuffin # Copyright 2024 tpart # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, version 3 of the License only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # class_name Game extends Node3D signal update_players(players: Dictionary) signal data_updated() signal player_set_input_enabled(b: bool) signal joined() signal left() var player_id: int = -1 var item_names: Array = [] var item_index_by_name: Dictionary = {} var tile_names: Array = [] var tile_index_by_name: Dictionary = {} var tile_collide: Array = [] var tile_interact: Array = [] var maps: Dictionary = {} var in_lobby := false var is_replay := false var is_joined := false var join_sent := false var players := {} @onready var camera: FollowCamera = $FollowCamera @onready var mp: Multiplayer = $Multiplayer @onready var map: Map = $Map @onready var environment = $WorldEnvironment @onready var debug_label = $Debug @onready var overlay = $Overlay @onready var lobby = $"../Lobby" @onready var server_message: ServerMessage = $ServerMessage func _ready(): if !Global.on_vulkan(): environment.environment.tonemap_exposure = 0.5 mp.replay_start.connect(func(): is_replay = true) mp.connection_closed.connect(func(reason: String): Global.error_message = reason; get_parent().replace_menu("res://menu/error.tscn") ) mp.init.connect(func(player_id_: int): player_id = player_id_) mp.data.connect(func( item_names_: Array, tile_names_: Array, tile_collide_: Array, tile_interact_: Array, maps_: Dictionary ): item_names = item_names_ tile_names = tile_names_ tile_collide = tile_collide_ tile_interact = tile_interact_ maps = maps_ tile_index_by_name = {} for id in tile_names.size(): tile_index_by_name[tile_names[id]] = id item_index_by_name.clear() for i in range(item_names.size()): item_index_by_name[item_names[i]] = i data_updated.emit() ) await mp.init if player_id == -1: push_error("multiplayer has not been initialized") mp.add_player.connect(func(player: int, player_name: String, pos: Vector2, character: int): var player_instance: Player if player == player_id: player_instance = ControllablePlayer.new(player, player_name, pos, character, self) camera.target = player_instance.movement_base player_set_input_enabled.connect(player_instance.set_input_enabled) is_joined = true joined.emit() else: player_instance = Player.new(player, player_name, pos, character, self) players[player] = player_instance add_child(player_instance) update_players.emit(players) ) mp.set_tile.connect(set_tile) mp.remove_tile.connect(func (pos): map.clear_tile(pos)) mp.position.connect(func(player: int, pos: Vector2, rot: float, boosting: bool): var player_instance: Player = players[player] player_instance.update_position(pos, rot, boosting) ) mp.remove_player.connect(func(id: int): var player: Player = players.get(id) if id == player_id: is_joined = false join_sent = false left.emit() camera.target = $Center if player != null: if player.hand != null: player.hand.queue_free() players.erase(id) player.queue_free() update_players.emit(players) ) mp.set_tile_item.connect(func(tile: Vector2i, item: int): var t: Tile = map.get_tile_instance(tile) var i = ItemFactory.produce(item_names[item], t.item_base) i.position = t.item_base.global_position add_child(i) i.name = item_names[item] t.set_item(i) ) mp.remove_tile_item.connect(func(tile: Vector2i): var t: Tile = map.get_tile_instance(tile) t.take_item().queue_free() ) mp.set_player_item.connect(func(player: int, item: int): var p: Player = players[player] var i = ItemFactory.produce(item_names[item], p.hand_base) add_child(i) i.name = item_names[item] p.set_item(i) ) mp.remove_player_item.connect(func(player: int): var p: Player = players[player] p.remove_item().queue_free() ) mp.take_item.connect(func(tile: Vector2i, player: int): var t: Tile = map.get_tile_instance(tile) var p: Player = players[player] p.take_item(t) ) mp.put_item.connect(func(player: int, tile: Vector2i): var t: Tile = map.get_tile_instance(tile) var p: Player = players[player] p.put_item(t) ) mp.pass_item_player.connect(func(from: int, to: int): var from_player: Player = players[from] var to_player: Player = players[to] from_player.pass_to(to_player) ) mp.pass_item_tile.connect(func(from: Vector2i, to: Vector2i): var from_tile: Tile = map.get_tile_instance(from) var to_tile: Tile = map.get_tile_instance(to) from_tile.pass_to(to_tile) ) mp.set_tile_progress.connect(func(tile: Vector2i, progress: float, warn: bool): var t: Tile = map.get_tile_instance(tile) t.progress(progress, warn) ) mp.set_tile_finished.connect(func(tile: Vector2i, warn: bool): var t: Tile = map.get_tile_instance(tile) t.finish(warn) ) mp.set_player_progress.connect(func(player: int, progress: float, warn: bool): var p: Player = players[player] p.progress(progress, warn) ) mp.set_player_finished.connect(func(player: int, warn: bool): var p: Player = players[player] p.finish(warn) ) mp.text_message.connect(func(player: int, text: String, persist: bool): var p: Player = players[player] p.text_message(text, persist) ) mp.item_message.connect(func(player: int, item: int, persist: bool): var p: Player = players[player] p.item_message(item_names[item], persist) ) mp.effect_message.connect(func(player: int, effect: String, persist: bool): var p: Player = players[player] p.effect_message(effect, persist) ) mp.clear_message.connect(func(player: int): var p: Player = players[player] p.clear_message() ) mp.set_ingame.connect(func (state, in_lobby_): in_lobby = in_lobby_ if state: map.gi_bake() await get_parent().menu_anim_open() map.autobake = true else: map.autobake = false await get_parent().menu_anim_exit() ) mp.server_message.connect( func(text): print(text) server_message.display_popup(text) ) mp.score.connect(overlay.update) mp.hide_score.connect(overlay.reset) mp.set_ingame.connect( func toggle_lobby(_state: bool, lobby_state: bool): lobby.visible = lobby_state ) func join(): join_sent = true mp.send_join(Global.profile["username"], Global.profile["character"]) func _process(delta): update_center() if is_replay and mp != null: mp.send_replay_tick(delta) if Global.get_setting("debug_info"): debug_label.show() debug_label.text = "%d FPS" % Engine.get_frames_per_second() else: debug_label.hide() func get_tile_collision(pos: Vector2i) -> bool: var t = map.get_tile_name(pos) if t == null: return true else: return tile_collide[tile_index_by_name[t]] func get_tile_interactive(pos: Vector2i) -> bool: var t = map.get_tile_name(pos) if t == null: return false else: return tile_interact[tile_index_by_name[t]] func set_tile(tile: Vector2i, kind = null, neighbors = null): if neighbors != null: neighbors = neighbors.map(func (x): return tile_names[x] if x != null else null) map.set_tile(tile, tile_names[kind], neighbors) func update_center(): if is_joined: return var sum: int = 0 var player_sum: int = 0 var center: Vector3 = Vector3(0., 0., 0.) var player_center: Vector3 = Vector3(0., 0., 0.) for v in players.values(): var p: Player = v if p.character_idx >= 0: player_sum += 1 player_center += p.movement_base.position sum += 1 center += p.movement_base.position var new_center: Vector3 = Vector3(0., 0., 0.) if player_sum > 0: new_center = player_center / player_sum elif sum > 0: new_center = center / sum else: var extents = map.extents() var map_center = ((extents[0] + extents[1]) / 2) + Vector2(.5, .5) new_center = Vector3(map_center.x, 0., map_center.y) $Center.position = new_center