From 9e8367b9d4e822cfe1199b896dcf8513578b8b3a Mon Sep 17 00:00:00 2001
From: Marko Lindqvist <cazfi74@gmail.com>
Date: Fri, 25 Apr 2025 02:28:40 +0300
Subject: [PATCH 39/39] Add accessarea.[ch] module

No user visible features provided yet

See RM #1324

Signed-off-by: Marko Lindqvist <cazfi74@gmail.com>
---
 common/Makefile.am        |   2 +
 common/accessarea.c       | 145 ++++++++++++++++++++++++++++++++++++++
 common/accessarea.h       |  48 +++++++++++++
 common/city.h             |   5 +-
 meson.build               |   1 +
 server/ruleset/ruleload.c |   5 ++
 server/srv_main.c         |   2 +
 7 files changed, 207 insertions(+), 1 deletion(-)
 create mode 100644 common/accessarea.c
 create mode 100644 common/accessarea.h

diff --git a/common/Makefile.am b/common/Makefile.am
index e45752ec55..8f066f540a 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -14,6 +14,8 @@ AM_CPPFLAGS = \
 	$(MAPIMG_WAND_CFLAGS) $(JANSSON_CFLAGS)
 
 libfreeciv_la_SOURCES = \
+		accessarea.c	\
+		accessarea.h	\
 		achievements.c	\
 		achievements.h	\
 		actions.c	\
diff --git a/common/accessarea.c b/common/accessarea.c
new file mode 100644
index 0000000000..1ee9a75e98
--- /dev/null
+++ b/common/accessarea.c
@@ -0,0 +1,145 @@
+/****************************************************************************
+ Freeciv - Copyright (C) 2004 - The Freeciv Team
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   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 General Public License for more details.
+****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <fc_config.h>
+#endif
+
+/* common/aicore */
+#include "pf_tools.h"
+
+/* common */
+#include "player.h"
+
+#include "accessarea.h"
+
+struct access_info {
+  const struct unit_type *access_unit;
+};
+
+static struct access_info ainfo = { nullptr };
+
+static struct aarea_list *aalist[MAX_NUM_PLAYERS];
+
+static void area_list_clear(struct aarea_list *alist);
+
+/*********************************************************************//**
+  Initialize access info.
+  @param aunit Access unit for the access info
+*************************************************************************/
+void access_info_init(const struct unit_type *aunit)
+{
+  if (aunit != nullptr) {
+    int i;
+
+    ainfo.access_unit = aunit;
+
+    for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+      aalist[i] = aarea_list_new();
+    }
+  }
+}
+
+/*********************************************************************//**
+  Close the access info.
+*************************************************************************/
+void access_info_close(void)
+{
+  int i;
+
+  if (ainfo.access_unit != nullptr) {
+    ainfo.access_unit = nullptr;
+
+    for (i = 0; i < MAX_NUM_PLAYERS; i++) {
+      area_list_clear(aalist[i]);
+    }
+  }
+}
+
+/*********************************************************************//**
+  Free access area list.
+  @param alist List to clear
+*************************************************************************/
+static void area_list_clear(struct aarea_list *alist)
+{
+  aarea_list_iterate(alist, parea) {
+    city_list_destroy(parea->cities);
+    free(parea);
+  } aarea_list_iterate_end;
+
+  aarea_list_destroy(alist);
+}
+
+/*********************************************************************//**
+  Construct access areas
+  @param nmap Map to use when determining access
+  @param plr  Player to construct areas for
+*************************************************************************/
+void access_areas_refresh(struct civ_map *nmap, struct player *plr)
+{
+  if (ainfo.access_unit != nullptr) {
+    int plridx = player_number(plr);
+    struct unit *access_unit;
+
+    area_list_clear(aalist[plridx]);
+    aalist[plridx] = aarea_list_new();
+
+    city_list_iterate(plr->cities, pcity) {
+      pcity->server.aarea = nullptr;
+    } city_list_iterate_end;
+
+    access_unit = unit_virtual_create(plr, nullptr,
+                                      ainfo.access_unit, 0);
+
+    city_list_iterate(plr->cities, pcity) {
+      if (pcity->server.aarea == nullptr) {
+        struct access_area *aarea = fc_malloc(sizeof(struct access_area));
+        struct pf_parameter parameter;
+        struct pf_map *pfm;
+
+        aarea->cities = city_list_new();
+        aarea->capital = is_capital(pcity);
+
+        pcity->server.aarea = aarea;
+        city_list_append(aarea->cities, pcity);
+        aarea_list_append(aalist[plridx], aarea);
+
+        unit_tile_set(access_unit, city_tile(pcity));
+        pft_fill_unit_parameter(&parameter, nmap, access_unit);
+        pfm = pf_map_new(&parameter);
+
+        city_list_iterate(plr->cities, pcity2) {
+          if (pcity2->server.aarea == nullptr) {
+            struct pf_path *path;
+
+            path = pf_map_path(pfm, city_tile(pcity2));
+            if (path != nullptr) {
+              pcity2->server.aarea = aarea;
+              city_list_append(aarea->cities, pcity2);
+
+              if (!aarea->capital && is_capital(pcity2)) {
+                aarea->capital = TRUE;
+              }
+            }
+
+            pf_path_destroy(path);
+          }
+        } city_list_iterate_end;
+
+        pf_map_destroy(pfm);
+      }
+    } city_list_iterate_end;
+
+    unit_virtual_destroy(access_unit);
+  }
+}
diff --git a/common/accessarea.h b/common/accessarea.h
new file mode 100644
index 0000000000..827358b01d
--- /dev/null
+++ b/common/accessarea.h
@@ -0,0 +1,48 @@
+/***********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   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 General Public License for more details.
+***********************************************************************/
+#ifndef FC__ACCESSAREA_H
+#define FC__ACCESSAREA_H
+
+/* common */
+#include "city.h"
+#include "fc_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct access_area {
+  const struct player *plr;
+  struct city_list *cities;
+  bool capital;
+};
+
+/* get 'struct access_area_list' and related functions: */
+#define SPECLIST_TAG aarea
+#define SPECLIST_TYPE struct access_area
+#include "speclist.h"
+
+#define aarea_list_iterate(aarealist, parea) \
+    TYPED_LIST_ITERATE(struct access_area, aarealist, parea)
+#define aarea_list_iterate_end  LIST_ITERATE_END
+
+void access_info_init(const struct unit_type *aunit);
+void access_info_close(void);
+
+void access_areas_refresh(struct civ_map *nmap, struct player *plr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* FC__ACCESSAREA_H */
diff --git a/common/city.h b/common/city.h
index 25006810fd..c7bc22d6a8 100644
--- a/common/city.h
+++ b/common/city.h
@@ -29,6 +29,7 @@ extern "C" {
 #include "workertask.h"
 #include "worklist.h"
 
+struct access_area;
 struct impr_type;
 struct unit;
 struct unit_list;
@@ -371,7 +372,7 @@ struct city {
   int turn_plague;              /* last turn with an illness in the city */
   int city_radius_sq;           /* current squared city radius */
 
-  /* turn states */
+  /* Turn states */
   int airlift;
   bool did_buy;
   bool did_sell;
@@ -450,6 +451,8 @@ struct city {
       void *ais[FREECIV_AI_MOD_LAST];
 
       struct vision *vision;
+
+      struct access_area *aarea;
     } server;
 
     struct {
diff --git a/meson.build b/meson.build
index 67510edfab..f40c598585 100644
--- a/meson.build
+++ b/meson.build
@@ -1410,6 +1410,7 @@ common_lib = library('freeciv',
   'common/scriptcore/luascript.c',
   'common/scriptcore/luascript_func.c',
   'common/scriptcore/luascript_signal.c',
+  'common/accessarea.c',
   'common/achievements.c',
   'common/actions.c',
   'common/actres.c',
diff --git a/server/ruleset/ruleload.c b/server/ruleset/ruleload.c
index 8b05bd4fa1..ccddcb2e6d 100644
--- a/server/ruleset/ruleload.c
+++ b/server/ruleset/ruleload.c
@@ -32,6 +32,7 @@
 #include "support.h"
 
 /* common */
+#include "accessarea.h"
 #include "achievements.h"
 #include "actions.h"
 #include "ai.h"
@@ -9201,6 +9202,7 @@ static void nullcheck_secfile_destroy(struct section_file *file)
 **************************************************************************/
 void rulesets_deinit(void)
 {
+  access_info_close();
   script_server_free();
   requirement_vector_free(&reqs_list);
 }
@@ -9225,6 +9227,7 @@ static bool load_rulesetdir(const char *rsdir, bool compat_mode,
   compat_info.compat_mode = compat_mode;
   compat_info.log_cb = logger;
 
+  access_info_close();
   game_ruleset_free();
   /* Reset the list of available player colors. */
   playercolor_free();
@@ -9432,6 +9435,8 @@ static bool load_rulesetdir(const char *rsdir, bool compat_mode,
     } unit_type_iterate_end;
     city_production_caravan_shields_init();
 
+    access_info_init(nullptr);
+
     /* Build advisors unit class cache corresponding to loaded rulesets */
     adv_units_ruleset_init();
     CALL_FUNC_EACH_AI(units_ruleset_init);
diff --git a/server/srv_main.c b/server/srv_main.c
index 67eab5c154..5aeda5aea3 100644
--- a/server/srv_main.c
+++ b/server/srv_main.c
@@ -66,6 +66,7 @@
 #include "citymap.h"
 
 /* common */
+#include "accessarea.h"
 #include "achievements.h"
 #include "calendar.h"
 #include "capstr.h"
@@ -1376,6 +1377,7 @@ static void begin_phase(bool is_new_phase)
   alive_phase_players_iterate(pplayer) {
     update_revolution(pplayer);
     update_capital(pplayer);
+    access_areas_refresh(&(wld.map), pplayer);
   } alive_phase_players_iterate_end;
 
   if (is_new_phase) {
-- 
2.47.2

