# 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 in_lobby_updated(in_lobby: bool) signal joined() signal left() enum SpectatingMode { CENTER, FREE, } 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: Array = [] var bot_algos: Array var in_lobby := false var is_replay := false var is_joined := false var join_sent := false var last_position := Vector2(0, 0) var players := {} var spectating_mode: SpectatingMode = SpectatingMode.CENTER @onready var camera: FollowCamera = $FollowCamera @onready var mp: Multiplayer = $Multiplayer @onready var map: Map = $Map @onready var lobby: Lobby = $"../Lobby" @onready var overlay: Overlay = $"../Overlay" @onready var popup_message: PopupMessage = $"../PopupMessage" @onready var menu: GameMenu = $".." @onready var follow_camera: FollowCamera = $FollowCamera func _ready(): 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.joined.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_: Array, bot_algos_: Array ): item_names = item_names_ tile_names = tile_names_ tile_collide = tile_collide_ tile_interact = tile_interact_ maps = maps_ bot_algos = bot_algos_ 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() ) 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) in_lobby_updated.connect(player_instance.onscreen_controls.in_lobby_updated) player_instance.onscreen_controls.in_lobby_updated(in_lobby) camera.target = player_instance.movement_base is_joined = true join_sent = 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.movement.connect(func(player: int, pos: Vector2, rot: float, boost: bool, _dir: Vector2): var player_instance: Player = players[player] player_instance.update_position(pos, rot, boost) if player == player_id: last_position = pos ) mp.movement_sync.connect(func(): if not players.has(player_id): return var player_instance: ControllablePlayer = players[player_id] player_instance.position_ = last_position ) 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] var removed = p.remove_item() if removed != null: removed.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, timeout_initial: float, timeout_remaining: float): var p: Player = players[player] p.text_message(text, timeout_initial, timeout_remaining) ) mp.item_message.connect(func(player: int, item: int, timeout_initial: float, timeout_remaining: float): var p: Player = players[player] p.item_message(item_names[item], timeout_initial, timeout_remaining) ) mp.effect_message.connect(func(player: int, effect: String, timeout_initial: float, timeout_remaining: float): var p: Player = players[player] p.effect_message(effect, timeout_initial, timeout_remaining) ) 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_ in_lobby_updated.emit(in_lobby) if state: map.gi_bake() await get_parent()._menu_open() map.autobake = true if in_lobby_: popup_message.lobby() else: popup_message.ingame() else: map.autobake = false await get_parent()._menu_exit() ) mp.server_message.connect( func(text): print(text) popup_message.display_server_msg(text) ) mp.score.connect(overlay.update) mp.set_ingame.connect(overlay.set_ingame) mp.set_ingame.connect(follow_camera.set_ingame) mp.set_ingame.connect( func toggle_lobby(_state: bool, lobby_state: bool): lobby.visible = lobby_state if lobby_state and not join_sent: join() ) mp.environment.connect($Environment.update) func join(): join_sent = true mp.send_join(Global.get_profile("username"), Global.get_profile("character")) func leave(): join_sent = false mp.send_leave(player_id) func _process(delta): update_center() if is_replay and mp != null: mp.send_replay_tick(delta) 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 if Input.get_vector("left", "right", "forward", "backwards").normalized().length() > .1: spectating_mode = SpectatingMode.FREE elif spectating_mode == SpectatingMode.FREE and Input.is_action_just_pressed("reset"): spectating_mode = SpectatingMode.CENTER match spectating_mode: SpectatingMode.CENTER: spectate_center() SpectatingMode.FREE: spectate_free() func spectate_center(): 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 func spectate_free(): var direction := Input.get_vector("left", "right", "forward", "backwards") direction = direction.rotated(-camera.angle_target) $Center.position += Vector3( direction.x, $Center.position.y, direction.y ) * get_process_delta_time() * 10. var extents = map.extents() $Center.position.x = clamp($Center.position.x, extents[0].x, extents[1].x) $Center.position.z = clamp($Center.position.z, extents[0].y, extents[1].y)