adding log camps

This commit is contained in:
hz
2026-05-18 20:42:49 -05:00
parent 3167558e04
commit bf75f02dbb
12 changed files with 365 additions and 35 deletions
@@ -14,6 +14,7 @@ var resources_request: HTTPRequest
var units_request: HTTPRequest
var train_unit_request: HTTPRequest
var claim_request: HTTPRequest
var build_request: HTTPRequest
var resources_timer: Timer
var session: Dictionary = {}
var escape_overlay: Control
@@ -31,9 +32,11 @@ var training_started_unix := 0.0
var training_completes_unix := 0.0
var training_completion_refresh_requested := false
var claim_button: Button
var build_log_camp_button: Button
var selected_q := 0
var selected_r := 0
var selected_can_claim := false
var selected_can_build_log_camp := false
func _ready() -> void:
@@ -104,9 +107,9 @@ func _build_ui() -> void:
legend.add_theme_constant_override("separation", 12)
top_content.add_child(legend)
legend.add_child(_legend_item(Color(0.78, 0.62, 0.19), "Home"))
legend.add_child(_legend_item(Color(0.26, 0.62, 0.32), "Yours"))
legend.add_child(_legend_item(Color(0.58, 0.28, 0.24), "Rival"))
legend.add_child(_legend_item(Color(1.0, 0.76, 0.16), "Home"))
legend.add_child(_legend_item(Color(0.16, 0.86, 0.92), "Yours"))
legend.add_child(_legend_item(Color(0.95, 0.22, 0.18), "Rival"))
legend.add_child(_legend_item(Color(0.38, 0.56, 0.36), "Open"))
legend.add_child(_legend_item(Color(0.045, 0.065, 0.065), "Unknown"))
@@ -181,8 +184,8 @@ func _build_ui() -> void:
bottom_content.add_child(coordinate_label)
unit_label = Label.new()
unit_label.text = "Scouts: --"
unit_label.custom_minimum_size = Vector2(88, 0)
unit_label.text = "Scouts: -- | Queue: --"
unit_label.custom_minimum_size = Vector2(142, 0)
unit_label.add_theme_font_size_override("font_size", 14)
unit_label.add_theme_color_override("font_color", Color(0.72, 0.82, 0.78))
bottom_content.add_child(unit_label)
@@ -217,6 +220,11 @@ func _build_ui() -> void:
claim_button.pressed.connect(_claim_selected_tile)
bottom_content.add_child(claim_button)
build_log_camp_button = _small_button("Build Log Camp")
build_log_camp_button.disabled = true
build_log_camp_button.pressed.connect(_build_log_camp)
bottom_content.add_child(build_log_camp_button)
http_request = HTTPRequest.new()
http_request.request_completed.connect(_on_home_request_completed)
add_child(http_request)
@@ -241,6 +249,10 @@ func _build_ui() -> void:
claim_request.request_completed.connect(_on_claim_request_completed)
add_child(claim_request)
build_request = HTTPRequest.new()
build_request.request_completed.connect(_on_build_request_completed)
add_child(build_request)
resources_timer = Timer.new()
resources_timer.wait_time = RESOURCE_REFRESH_SECONDS
resources_timer.autostart = false
@@ -259,8 +271,11 @@ func _on_tile_selected(q: int, r: int, terrain: String) -> void:
selected_q = q
selected_r = r
selected_can_claim = terrain.contains("Unclaimed")
selected_can_build_log_camp = terrain.contains("Forest") and terrain.contains("Your territory") and not terrain.contains("Log Camp")
if claim_button:
claim_button.disabled = not selected_can_claim
if build_log_camp_button:
build_log_camp_button.disabled = not selected_can_build_log_camp
if coordinate_label:
coordinate_label.text = "Selected q:%d r:%d | %s territory" % [q, r, terrain]
@@ -412,7 +427,9 @@ func _on_resources_request_completed(result: int, response_code: int, _headers:
var resources: Dictionary = parsed
var gold: int = int(resources.get("gold", 0))
var gold_per_minute: int = int(resources.get("goldPerMinute", 0))
gold_label.text = "Gold: %d (+%d/min)" % [gold, gold_per_minute]
var wood: int = int(resources.get("wood", 0))
var wood_per_minute: int = int(resources.get("woodPerMinute", 0))
gold_label.text = "Gold: %d (+%d/min) | Wood: %d (+%d/min)" % [gold, gold_per_minute, wood, wood_per_minute]
var anchor_text: String = str(resources.get("lastGoldGeneratedAtUtc", ""))
gold_tick_anchor_unix = _parse_utc_timestamp(anchor_text)
@@ -452,12 +469,13 @@ func _on_units_request_completed(result: int, response_code: int, _headers: Pack
return
var units: Dictionary = parsed
unit_label.text = "Scouts: %d" % int(units.get("completedScouts", 0))
var training: Array = []
var raw_training = units.get("training", [])
if typeof(raw_training) == TYPE_ARRAY:
training = raw_training
unit_label.text = "Scouts: %d | Queue: %d" % [int(units.get("completedScouts", 0)), training.size()]
if training.is_empty():
training_started_unix = 0.0
training_completes_unix = 0.0
@@ -572,6 +590,55 @@ func _on_claim_request_completed(result: int, response_code: int, _headers: Pack
_refresh_player_state()
func _build_log_camp() -> void:
if not selected_can_build_log_camp:
coordinate_label.text = "Select controlled Forest territory without a building."
return
var access_token: String = str(session.get("accessToken", ""))
if access_token.is_empty():
coordinate_label.text = "No auth token available. Log Camp was not built."
return
if build_request.get_http_client_status() != HTTPClient.STATUS_DISCONNECTED:
return
build_log_camp_button.disabled = true
coordinate_label.text = "Building Log Camp at q:%d r:%d..." % [selected_q, selected_r]
var error: int = build_request.request(
LOCATIONS_BASE_URL + "/api/locations/build",
PackedStringArray([
"Content-Type: application/json",
"Authorization: Bearer %s" % access_token
]),
HTTPClient.METHOD_POST,
JSON.stringify({ "q": selected_q, "r": selected_r, "buildingType": "LogCamp" })
)
if error != OK:
build_log_camp_button.disabled = false
coordinate_label.text = "Could not contact the locations service."
func _on_build_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
var text: String = body.get_string_from_utf8()
if result != HTTPRequest.RESULT_SUCCESS:
coordinate_label.text = "Log Camp build request failed."
build_log_camp_button.disabled = not selected_can_build_log_camp
return
if response_code < 200 or response_code >= 300:
coordinate_label.text = "Log Camp build failed: %s" % text
build_log_camp_button.disabled = not selected_can_build_log_camp
_refresh_player_state()
return
selected_can_build_log_camp = false
build_log_camp_button.disabled = true
coordinate_label.text = "Log Camp built. Wood production increased."
_load_visible_locations()
_refresh_player_state()
func _update_tick_bar() -> void:
if not tick_bar or not tick_label:
return
@@ -6,6 +6,9 @@ signal tile_selected(q: int, r: int, terrain: String)
const HEX_SIZE := 42.0
const ISO_Y_SCALE := 0.56
const SQRT_3 := 1.7320508075688772
const INFLUENCE_RADIUS_X := 54.0
const INFLUENCE_RADIUS_Y := 30.0
const INFLUENCE_LINK_WIDTH := 54.0
var camera_offset: Vector2 = Vector2.ZERO
var zoom: float = 1.0
@@ -101,10 +104,11 @@ func _draw() -> void:
return _tile_center(int(a["q"]), int(a["r"])).y < _tile_center(int(b["q"]), int(b["r"])).y
)
_draw_control_regions(sorted_tiles)
for tile in sorted_tiles:
_draw_tile(tile)
func _draw_grid_lines() -> void:
var line_color: Color = Color(0.12, 0.17, 0.17, 0.35)
for x in range(-8, 9):
@@ -155,10 +159,55 @@ func _draw_tile(tile: Dictionary) -> void:
draw_polyline(_closed_points(points), Color(0.95, 0.76, 0.28), 4.0)
elif coord == home_tile:
draw_polyline(_closed_points(points), Color(1.0, 0.88, 0.38), 3.0)
elif is_owned:
draw_polyline(_closed_points(points), Color(0.48, 0.95, 0.55), 3.0)
elif is_rival:
draw_polyline(_closed_points(points), Color(0.94, 0.36, 0.29), 3.0)
var visible_location: Dictionary = visible_tiles.get(key, {})
var building_type: String = str(visible_location.get("buildingType", ""))
if building_type == "LogCamp":
_draw_log_camp_marker(center, height)
func _draw_control_regions(sorted_tiles: Array) -> void:
for control in ["owned", "rival"]:
var control_tiles: Array = sorted_tiles.filter(func(tile: Dictionary) -> bool:
return _tile_control(_tile_key(int(tile["q"]), int(tile["r"]))) == control
)
if control_tiles.is_empty():
continue
_draw_control_layer(control_tiles, control, 1.34, 0.16, INFLUENCE_LINK_WIDTH + 18.0)
_draw_control_layer(control_tiles, control, 1.08, 0.28, INFLUENCE_LINK_WIDTH)
_draw_control_layer(control_tiles, control, 0.82, 0.20, INFLUENCE_LINK_WIDTH - 16.0)
func _draw_control_layer(control_tiles: Array, control: String, layer_scale: float, alpha: float, link_width: float) -> void:
var control_color: Color = _control_color(control)
var layer_color := Color(control_color.r, control_color.g, control_color.b, alpha)
var tile_lookup := {}
for tile in control_tiles:
var q: int = int(tile["q"])
var r: int = int(tile["r"])
tile_lookup[_tile_key(q, r)] = true
for tile in control_tiles:
var q: int = int(tile["q"])
var r: int = int(tile["r"])
var center: Vector2 = _tile_center(q, r)
for neighbor in _neighbor_coordinates(q, r):
var neighbor_key: String = _tile_key(neighbor.x, neighbor.y)
if not tile_lookup.has(neighbor_key):
continue
if _tile_key(q, r) > neighbor_key:
continue
var neighbor_center: Vector2 = _tile_center(neighbor.x, neighbor.y)
draw_line(center, neighbor_center, layer_color, link_width * zoom * layer_scale)
draw_colored_polygon(
_ellipse_points(center, INFLUENCE_RADIUS_X * zoom * layer_scale, INFLUENCE_RADIUS_Y * zoom * layer_scale),
layer_color
)
func _update_hover(mouse_position: Vector2) -> void:
@@ -202,11 +251,15 @@ func _screen_point(world_point: Vector2) -> Vector2:
func _hex_points(center: Vector2, height: float) -> PackedVector2Array:
return _hex_points_scaled(center, height, 1.0)
func _hex_points_scaled(center: Vector2, height: float, point_scale: float) -> PackedVector2Array:
var points: PackedVector2Array = PackedVector2Array()
var top_lift: Vector2 = Vector2(0, -height * zoom)
for i in range(6):
var angle: float = deg_to_rad(60.0 * i + 30.0)
var local: Vector2 = Vector2(cos(angle) * HEX_SIZE, sin(angle) * HEX_SIZE * ISO_Y_SCALE) * zoom
var local: Vector2 = Vector2(cos(angle) * HEX_SIZE * point_scale, sin(angle) * HEX_SIZE * ISO_Y_SCALE * point_scale) * zoom
points.append(center + local + top_lift)
return points
@@ -218,6 +271,33 @@ func _closed_points(points: PackedVector2Array) -> PackedVector2Array:
return closed
func _ellipse_points(center: Vector2, radius_x: float, radius_y: float) -> PackedVector2Array:
var points: PackedVector2Array = PackedVector2Array()
for i in range(32):
var angle: float = TAU * float(i) / 32.0
points.append(center + Vector2(cos(angle) * radius_x, sin(angle) * radius_y))
return points
func _draw_log_camp_marker(center: Vector2, height: float) -> void:
var marker_center: Vector2 = center + Vector2(0, -height * zoom - 2.0)
var width: float = 18.0 * zoom
var marker_height: float = 10.0 * zoom
var hut_color := Color(0.46, 0.29, 0.14)
var roof_color := Color(0.28, 0.17, 0.09)
var log_color := Color(0.72, 0.52, 0.28)
var roof := PackedVector2Array([
marker_center + Vector2(-width * 0.55, -marker_height * 0.12),
marker_center + Vector2(0, -marker_height * 0.75),
marker_center + Vector2(width * 0.55, -marker_height * 0.12)
])
draw_colored_polygon(roof, roof_color)
draw_rect(Rect2(marker_center + Vector2(-width * 0.38, -marker_height * 0.08), Vector2(width * 0.76, marker_height * 0.62)), hut_color, true)
draw_line(marker_center + Vector2(-width * 0.48, marker_height * 0.62), marker_center + Vector2(width * 0.48, marker_height * 0.62), log_color, 3.0 * zoom)
draw_line(marker_center + Vector2(-width * 0.38, marker_height * 0.86), marker_center + Vector2(width * 0.38, marker_height * 0.86), log_color.darkened(0.12), 3.0 * zoom)
func _tile_data(q: int, r: int) -> Dictionary:
for tile in tiles:
if int(tile["q"]) == q and int(tile["r"]) == r:
@@ -269,27 +349,61 @@ func _ownership_fill_color(base_color: Color, coord: Vector2i, tile_visible: boo
if not tile_visible:
return Color(0.045, 0.065, 0.065)
if coord == home_tile:
return Color(0.78, 0.62, 0.19)
return base_color.lerp(Color(0.95, 0.72, 0.18), 0.38)
if is_owned:
return Color(0.26, 0.62, 0.32)
return base_color.lerp(Color(0.20, 0.68, 0.72), 0.32)
if is_rival:
return Color(0.58, 0.28, 0.24)
return base_color.lerp(Color(0.72, 0.22, 0.18), 0.35)
return base_color
func _tile_control(key: String) -> String:
if owned_tiles.has(key):
return "owned"
if rival_tiles.has(key):
return "rival"
return ""
func _control_color(control: String) -> Color:
match control:
"owned":
return Color(0.16, 0.86, 0.92)
"rival":
return Color(0.95, 0.22, 0.18)
_:
return Color(0.4, 0.5, 0.5)
func _neighbor_coordinates(q: int, r: int) -> Array[Vector2i]:
return [
Vector2i(q + 1, r),
Vector2i(q + 1, r - 1),
Vector2i(q, r - 1),
Vector2i(q - 1, r),
Vector2i(q - 1, r + 1),
Vector2i(q, r + 1)
]
func _tile_display_label(key: String, data: Dictionary) -> String:
var terrain: String = str(data.get("terrain", "Open"))
var building_type: String = str(data.get("buildingType", ""))
var building_label: String = ""
if building_type == "LogCamp":
building_label = " | Log Camp"
if home_tile == Vector2i(int(data.get("q", 0)), int(data.get("r", 0))):
return "%s | Your home" % terrain
return "%s | Your home%s" % [terrain, building_label]
if owned_tiles.has(key):
return "%s | Your territory" % terrain
return "%s | Your territory%s" % [terrain, building_label]
if rival_tiles.has(key):
var rival_data: Dictionary = rival_tiles[key]
var owner_username: String = _owner_username(rival_data)
if owner_username.is_empty():
return "%s | Unclaimed" % terrain
return "%s | Controlled by %s" % [terrain, owner_username]
return "%s | Unclaimed" % terrain
return "%s | Unclaimed%s" % [terrain, building_label]
return "%s | Controlled by %s%s" % [terrain, owner_username, building_label]
return "%s | Unclaimed%s" % [terrain, building_label]
func _owner_username(location: Dictionary) -> String: