diff --git a/src/building/connectable.c b/src/building/connectable.c index 3451d71efe..3e0529f90a 100644 --- a/src/building/connectable.c +++ b/src/building/connectable.c @@ -47,6 +47,7 @@ static const building_type connectable_buildings[] = { BUILDING_HEDGE_GATE_DARK, BUILDING_HEDGE_GATE_LIGHT, BUILDING_PALISADE_GATE, + BUILDING_NATIVE_PALISADE, }; static const int MAX_CONNECTABLE_BUILDINGS = sizeof(connectable_buildings) / sizeof(building_type); @@ -402,12 +403,13 @@ int building_connectable_get_garden_gate_offset(int grid_offset) static int is_palisade_wall_or_gate(building_type type) { - return type == BUILDING_PALISADE || type == BUILDING_PALISADE_GATE; + return type == BUILDING_PALISADE || type == BUILDING_PALISADE_GATE || + type == BUILDING_NATIVE_PALISADE; } static int is_palisade_wall(building_type type) { - return type == BUILDING_PALISADE; + return type == BUILDING_PALISADE || type == BUILDING_NATIVE_PALISADE; } int building_connectable_get_palisade_offset(int grid_offset) @@ -487,6 +489,7 @@ int building_connectable_num_variants(building_type type) case BUILDING_ROOFED_GARDEN_WALL: case BUILDING_PANELLED_GARDEN_WALL: case BUILDING_PALISADE: + case BUILDING_NATIVE_PALISADE: return BUILDING_CONNECTABLE_ROTATION_LIMIT_HEDGES; default: return BUILDING_CONNECTABLE_ROTATION_LIMIT_PATHS; diff --git a/src/building/figure.c b/src/building/figure.c index 24b61366ed..f0644a5cfe 100644 --- a/src/building/figure.c +++ b/src/building/figure.c @@ -2130,7 +2130,12 @@ static void update_native_crop_progress(building *b) if (b->data.industry.progress >= 5) { b->data.industry.progress = 0; } - map_image_set(b->grid_offset, image_group(GROUP_BUILDING_FARM_CROPS) + b->data.industry.progress); + int variant = b->subtype.orientation; + if (variant < 0 || variant > 5) { + variant = 0; + } + map_image_set(b->grid_offset, + image_group(GROUP_BUILDING_FARM_CROPS) + variant * 5 + b->data.industry.progress); } void building_figure_generate(void) diff --git a/src/building/image.c b/src/building/image.c index fb2e819bc0..946dca9cda 100644 --- a/src/building/image.c +++ b/src/building/image.c @@ -604,8 +604,53 @@ int building_image_get(const building *b) default: return assets_get_image_id("Terrain_Maps", "Native_Hut_Central_01") + (random_byte() & 1); } + case BUILDING_NATIVE_HUT_ALT_2: + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + return assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Northern_01") + (random_byte() % 3); + case CLIMATE_DESERT: + return assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Southern_01") + (random_byte() % 3); + default: + return assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Central_01") + (random_byte() % 3); + } case BUILDING_NATIVE_MEETING: return image_group(GROUP_BUILDING_NATIVE) + 2; + case BUILDING_NATIVE_MEETING_ALT: + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + return assets_get_image_id("Terrain_Maps", "Native_Meeting_Hut_Northern_01"); + case CLIMATE_DESERT: + return assets_get_image_id("Terrain_Maps", "Native_Meeting_Hut_Southern_01"); + default: + return assets_get_image_id("Terrain_Maps", "Native_Meeting_Hut_Central_01"); + } + case BUILDING_NATIVE_MEETING_ALT_2: + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + return assets_get_image_id("Terrain_Maps", "Hellenised_Meeting_Hut_Northern_01"); + case CLIMATE_DESERT: + return assets_get_image_id("Terrain_Maps", "Hellenised_Meeting_Hut_Southern_01"); + default: + return assets_get_image_id("Terrain_Maps", "Hellenised_Meeting_Hut_Central_01"); + } + case BUILDING_NATIVE_WELL: + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + return assets_get_image_id("Terrain_Maps", "Native_Well_Northern"); + case CLIMATE_DESERT: + return assets_get_image_id("Terrain_Maps", "Native_Well_Southern"); + default: + return assets_get_image_id("Terrain_Maps", "Native_Well_Central"); + } + case BUILDING_NATIVE_PALISADE: + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + return assets_get_image_id("Military", "Pal Wall N 01") + building_connectable_get_palisade_offset(b->grid_offset); + case CLIMATE_DESERT: + return assets_get_image_id("Military", "Pal Wall S 01") + building_connectable_get_palisade_offset(b->grid_offset); + default: + return assets_get_image_id("Military", "Pal Wall C 01") + building_connectable_get_palisade_offset(b->grid_offset); + } case BUILDING_NATIVE_CROPS: return image_group(GROUP_BUILDING_FARM_CROPS); case BUILDING_GRAND_TEMPLE_CERES: diff --git a/src/building/properties.c b/src/building/properties.c index 7a6c45e6a5..470bd0f9d1 100644 --- a/src/building/properties.c +++ b/src/building/properties.c @@ -2212,6 +2212,55 @@ static building_properties properties[BUILDING_TYPE_MAX] = { .building_model_data = {.cost = 200, .desirability_value = -2, .desirability_step = 1, .desirability_step_size = 1, .desirability_range = 3, .laborers = 12} }, + [BUILDING_NATIVE_WELL] = { + .size = 1, + .fire_proof = 1, + .custom_asset.group = "Terrain_Maps", + .custom_asset.id = "Native_Well_Central", + .event_data.attr = "native_well", + .building_model_data = {.cost = 0, .desirability_value = 0, .desirability_step = 0, + .desirability_step_size = 0, .desirability_range = 0, .laborers = 0} + }, + [BUILDING_NATIVE_MEETING_ALT] = { + .size = 2, + .sound_id = SOUND_CITY_NATIVE_HUT, + .fire_proof = 1, + .custom_asset.group = "Terrain_Maps", + .custom_asset.id = "Native_Meeting_Hut_Central_01", + .event_data.attr = "native_meeting_alt", + .building_model_data = {.cost = 50, .desirability_value = 0, .desirability_step = 0, + .desirability_step_size = 0, .desirability_range = 0, .laborers = 0} + }, + [BUILDING_NATIVE_HUT_ALT_2] = { + .size = 1, + .sound_id = SOUND_CITY_NATIVE_HUT, + .fire_proof = 1, + .custom_asset.group = "Terrain_Maps", + .custom_asset.id = "Hellenised_Hut_Central_01", + .event_data.attr = "native_hut_alt_2", + .building_model_data = {.cost = 50, .desirability_value = 0, .desirability_step = 0, + .desirability_step_size = 0, .desirability_range = 0, .laborers = 0} + }, + [BUILDING_NATIVE_MEETING_ALT_2] = { + .size = 2, + .sound_id = SOUND_CITY_NATIVE_HUT, + .fire_proof = 1, + .custom_asset.group = "Terrain_Maps", + .custom_asset.id = "Hellenised_Meeting_Hut_Central_01", + .event_data.attr = "native_meeting_alt_2", + .building_model_data = {.cost = 50, .desirability_value = 0, .desirability_step = 0, + .desirability_step_size = 0, .desirability_range = 0, .laborers = 0} + }, + [BUILDING_NATIVE_PALISADE] = { + .size = 1, + .fire_proof = 1, + .draw_desirability_range = 1, + .custom_asset.group = "Military", + .custom_asset.id = "Pal Wall C 01", + .event_data.attr = "native_palisade", + .building_model_data = {.cost = 0, .desirability_value = 0, .desirability_step = 0, + .desirability_step_size = 0, .desirability_range = 0, .laborers = 0} + }, }; void building_properties_init(void) diff --git a/src/building/type.h b/src/building/type.h index b773874582..95b32ca7fa 100644 --- a/src/building/type.h +++ b/src/building/type.h @@ -223,8 +223,13 @@ typedef enum { BUILDING_NATIVE_DECORATION = 209, BUILDING_REPAIR_LAND = 210, // tool, not an actual building BUILDING_HIGHWAY_STATION = 211, + BUILDING_NATIVE_WELL = 212, + BUILDING_NATIVE_MEETING_ALT = 213, + BUILDING_NATIVE_HUT_ALT_2 = 214, + BUILDING_NATIVE_MEETING_ALT_2 = 215, + BUILDING_NATIVE_PALISADE = 216, // helper constants - BUILDING_TYPE_MAX = 212 + BUILDING_TYPE_MAX = 217 } building_type; /** diff --git a/src/core/image.c b/src/core/image.c index da42721838..bea252a08a 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -2,6 +2,7 @@ #include "assets/assets.h" #include "building/building.h" +#include "building/connectable.h" #include "building/image.h" #include "core/buffer.h" #include "core/file.h" @@ -653,14 +654,58 @@ static void update_native_images(int old_climate, int new_climate) for (building *b = building_first_of_type(BUILDING_NATIVE_HUT_ALT); b; b = b->next_of_type) { map_image_set(b->grid_offset, alt_native_hut_new_image_id + map_image_at(b->grid_offset) - alt_native_hut_old_image_id); } - building_type native_buildings[] = { BUILDING_NATIVE_DECORATION, BUILDING_NATIVE_MONUMENT, BUILDING_NATIVE_WATCHTOWER }; - for (int i = 0; i < sizeof(native_buildings) / sizeof(native_buildings[0]); i++) { + + // Hellenised Hut alt 2: 3 variants per climate, preserve variant index across climate change. + int hut_alt_2_old_image_id; + int hut_alt_2_new_image_id; + switch (old_climate) { + case CLIMATE_NORTHERN: + hut_alt_2_old_image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Northern_01"); break; + case CLIMATE_DESERT: + hut_alt_2_old_image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Southern_01"); break; + default: + hut_alt_2_old_image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Central_01"); + } + switch (new_climate) { + case CLIMATE_NORTHERN: + hut_alt_2_new_image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Northern_01"); break; + case CLIMATE_DESERT: + hut_alt_2_new_image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Southern_01"); break; + default: + hut_alt_2_new_image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Central_01"); + } + for (building *b = building_first_of_type(BUILDING_NATIVE_HUT_ALT_2); b; b = b->next_of_type) { + map_image_set(b->grid_offset, hut_alt_2_new_image_id + map_image_at(b->grid_offset) - hut_alt_2_old_image_id); + } + + building_type native_buildings[] = { + BUILDING_NATIVE_DECORATION, BUILDING_NATIVE_MONUMENT, BUILDING_NATIVE_WATCHTOWER, + BUILDING_NATIVE_WELL, + }; + for (int i = 0; i < (int) (sizeof(native_buildings) / sizeof(native_buildings[0])); i++) { building_type type = native_buildings[i]; int image_id = building_image_get_for_type(type); for (building *b = building_first_of_type(type); b; b = b->next_of_type) { map_building_tiles_add(b->id, b->x, b->y, b->size, image_id, TERRAIN_BUILDING); } } + + building_type meeting_buildings[] = { + BUILDING_NATIVE_MEETING_ALT, BUILDING_NATIVE_MEETING_ALT_2, + }; + for (int i = 0; i < (int) (sizeof(meeting_buildings) / sizeof(meeting_buildings[0])); i++) { + building_type type = meeting_buildings[i]; + int image_id = building_image_get_for_type(type); + for (building *b = building_first_of_type(type); b; b = b->next_of_type) { + map_building_tiles_add(b->id, b->x, b->y, b->size, image_id, TERRAIN_BUILDING); + } + } + + // Palisade: re-set image based on new climate, then refresh connectivity offsets + for (building *b = building_first_of_type(BUILDING_NATIVE_PALISADE); b; b = b->next_of_type) { + map_image_set(b->grid_offset, building_image_get(b)); + } + building_connectable_update_connections(); } static void fix_animation_offsets(void) diff --git a/src/core/lang.c b/src/core/lang.c index 02b576b13e..7b9262e4ae 100644 --- a/src/core/lang.c +++ b/src/core/lang.c @@ -639,6 +639,30 @@ const uint8_t *lang_get_string(int group, int index) return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_MONUMENT); case TR_EDITOR_SCENARIO_BUILDING_NATIVE_WATCHTOWER: return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_WATCHTOWER); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_WELL: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_WELL); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT_2: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT_2); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT_2: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT_2); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_PALISADE: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_PALISADE); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELDS: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELDS); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_WHEAT: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_WHEAT); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VEGETABLES: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VEGETABLES); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_FRUIT: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_FRUIT); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_OLIVE: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_OLIVE); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VINES: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VINES); + case TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_PIG: + return translation_for(TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_PIG); case TR_EDITOR_TOOL_EARTHQUAKE_POINT: return translation_for(TR_EDITOR_TOOL_EARTHQUAKE_POINT); case TR_EDITOR_TOOL_EARTHQUAKE_CUSTOM: diff --git a/src/editor/tool.c b/src/editor/tool.c index c87c397924..bd96e94034 100644 --- a/src/editor/tool.c +++ b/src/editor/tool.c @@ -1,9 +1,12 @@ #include "tool.h" #include "assets/assets.h" +#include "building/building.h" +#include "building/connectable.h" #include "building/image.h" #include "building/construction_routed.h" #include "core/image.h" +#include "core/image_group.h" #include "core/image_group_editor.h" #include "core/random.h" #include "editor/tool_restriction.h" @@ -20,6 +23,7 @@ #include "scenario/editor.h" #include "scenario/editor_events.h" #include "scenario/editor_map.h" +#include "scenario/property.h" #include "city/warning.h" #include "widget/map_editor.h" #include "widget/minimap.h" @@ -49,6 +53,11 @@ tool_type editor_tool_type(void) return data.type; } +int editor_tool_id(void) +{ + return data.id; +} + int editor_tool_is_active(void) { return data.active; @@ -120,7 +129,8 @@ void editor_tool_foreach_brush_tile(void (*callback)(const void *user_data, int int editor_tool_is_updatable(void) { - return data.type == TOOL_ROAD || data.type == TOOL_SELECT_LAND;; + return data.type == TOOL_ROAD || data.type == TOOL_SELECT_LAND || + data.type == TOOL_NATIVE_PALISADE; } int editor_tool_is_in_use(void) @@ -438,9 +448,40 @@ static void place_building(const map_tile *tile) break; case TOOL_NATIVE_FIELD: type = BUILDING_NATIVE_CROPS; + // Always display wheat in editor; variant is stored on the building and + // encoded into the tile image only at save time image_id = image_group(GROUP_EDITOR_BUILDING_CROPS); size = 1; break; + case TOOL_NATIVE_WELL: + type = BUILDING_NATIVE_WELL; + size = 1; + image_id = building_image_get_for_type(type); + break; + case TOOL_NATIVE_MEETING_ALT: + type = BUILDING_NATIVE_MEETING_ALT; + size = 2; + image_id = building_image_get_for_type(type); + break; + case TOOL_NATIVE_HUT_ALT_2: + type = BUILDING_NATIVE_HUT_ALT_2; + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Northern_01") + (random_byte() % 3); + break; + case CLIMATE_DESERT: + image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Southern_01") + (random_byte() % 3); + break; + default: + image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Central_01") + (random_byte() % 3); + } + size = 1; + break; + case TOOL_NATIVE_MEETING_ALT_2: + type = BUILDING_NATIVE_MEETING_ALT_2; + size = 2; + image_id = building_image_get_for_type(type); + break; case TOOL_NATIVE_DECORATION: type = BUILDING_NATIVE_DECORATION; size = 1; @@ -462,6 +503,11 @@ static void place_building(const map_tile *tile) if (editor_tool_can_place_building(tile, size * size, 0)) { building *b = building_create(type, tile->x, tile->y); + if (type == BUILDING_NATIVE_CROPS) { + // Stash variant index (0..5 → wheat/vegetables/fruit/olive/vines/pig) + // in an otherwise-unused subtype slot. Encoded onto the tile at save time. + b->subtype.orientation = (data.id >= 0 && data.id < 6) ? data.id : 0; + } map_building_tiles_add(b->id, tile->x, tile->y, size, image_id, TERRAIN_BUILDING); scenario_editor_set_as_unsaved(); } else { @@ -512,6 +558,32 @@ static void place_road(const map_tile *start_tile, const map_tile *end_tile) } } +static void place_native_palisade(const map_tile *start_tile, const map_tile *end_tile) +{ + int x_min, y_min, x_max, y_max; + map_grid_start_end_to_area(start_tile->x, start_tile->y, end_tile->x, end_tile->y, + &x_min, &y_min, &x_max, &y_max); + int items_placed = 0; + for (int y = y_min; y <= y_max; y++) { + for (int x = x_min; x <= x_max; x++) { + int grid_offset = map_grid_offset(x, y); + if (map_terrain_is(grid_offset, TERRAIN_NOT_CLEAR)) { + continue; + } + building *b = building_create(BUILDING_NATIVE_PALISADE, x, y); + map_building_tiles_add(b->id, b->x, b->y, b->size, + building_image_get(b), TERRAIN_BUILDING); + items_placed++; + } + } + if (items_placed > 0) { + building_connectable_update_connections(); + scenario_editor_set_as_unsaved(); + } else { + city_warning_show(WARNING_EDITOR_CANNOT_PLACE, NEW_WARNING_SLOT); + } +} + void editor_tool_end_use(const map_tile *tile) { if (!data.build_in_progress) { @@ -550,11 +622,18 @@ void editor_tool_end_use(const map_tile *tile) case TOOL_NATIVE_FIELD: case TOOL_NATIVE_HUT: case TOOL_NATIVE_HUT_ALT: + case TOOL_NATIVE_HUT_ALT_2: + case TOOL_NATIVE_MEETING_ALT: + case TOOL_NATIVE_MEETING_ALT_2: + case TOOL_NATIVE_WELL: case TOOL_NATIVE_MONUMENT: case TOOL_NATIVE_WATCHTOWER: case TOOL_NATIVE_DECORATION: place_building(tile); break; + case TOOL_NATIVE_PALISADE: + place_native_palisade(&data.start_tile, tile); + break; case TOOL_RAISE_LAND: case TOOL_LOWER_LAND: update_terrain_after_elevation_changes(); diff --git a/src/editor/tool.h b/src/editor/tool.h index 11dea63863..8c26c70b5f 100644 --- a/src/editor/tool.h +++ b/src/editor/tool.h @@ -33,10 +33,16 @@ typedef enum { TOOL_EARTHQUAKE_CUSTOM = 30, TOOL_EARTHQUAKE_CUSTOM_REMOVE = 31, TOOL_NATIVE_RUINS = 32, - TOOL_SELECT_LAND = 33 + TOOL_SELECT_LAND = 33, + TOOL_NATIVE_WELL = 34, + TOOL_NATIVE_MEETING_ALT = 35, + TOOL_NATIVE_HUT_ALT_2 = 36, + TOOL_NATIVE_MEETING_ALT_2 = 37, + TOOL_NATIVE_PALISADE = 38 } tool_type; tool_type editor_tool_type(void); +int editor_tool_id(void); int editor_tool_is_active(void); void editor_tool_deactivate(void); void editor_tool_set_type(tool_type tool); diff --git a/src/figure/formation_enemy.c b/src/figure/formation_enemy.c index 4c9f66c181..58f627963c 100644 --- a/src/figure/formation_enemy.c +++ b/src/figure/formation_enemy.c @@ -273,9 +273,12 @@ static int get_structures_on_native_land(int *dst_x, int *dst_y) building_type native_buildings[] = { BUILDING_NATIVE_MEETING, + BUILDING_NATIVE_MEETING_ALT, + BUILDING_NATIVE_MEETING_ALT_2, BUILDING_NATIVE_WATCHTOWER, BUILDING_NATIVE_HUT, - BUILDING_NATIVE_HUT_ALT + BUILDING_NATIVE_HUT_ALT, + BUILDING_NATIVE_HUT_ALT_2 }; int min_distance = INFINITE; @@ -326,11 +329,16 @@ static void set_native_target_building(formation *m) case BUILDING_MISSION_POST: case BUILDING_NATIVE_HUT: case BUILDING_NATIVE_HUT_ALT: + case BUILDING_NATIVE_HUT_ALT_2: case BUILDING_NATIVE_CROPS: case BUILDING_NATIVE_MEETING: + case BUILDING_NATIVE_MEETING_ALT: + case BUILDING_NATIVE_MEETING_ALT_2: case BUILDING_NATIVE_MONUMENT: case BUILDING_NATIVE_WATCHTOWER: case BUILDING_NATIVE_DECORATION: + case BUILDING_NATIVE_WELL: + case BUILDING_NATIVE_PALISADE: case BUILDING_WAREHOUSE: case BUILDING_FORT_ARCHERS: case BUILDING_FORT_LEGIONARIES: diff --git a/src/game/file_editor.c b/src/game/file_editor.c index b19a614770..4eb9e2e2f5 100644 --- a/src/game/file_editor.c +++ b/src/game/file_editor.c @@ -1,6 +1,7 @@ #include "file_editor.h" #include "assets/assets.h" +#include "building/building.h" #include "building/construction.h" #include "building/image.h" #include "building/menu.h" @@ -10,6 +11,7 @@ #include "city/victory.h" #include "city/view.h" #include "core/image.h" +#include "core/image_group.h" #include "core/image_group_editor.h" #include "empire/editor.h" #include "empire/empire.h" @@ -186,6 +188,24 @@ int game_file_editor_write_scenario(const char *scenario_file) int image_native_decoration = building_image_get_for_type(BUILDING_NATIVE_DECORATION); int image_native_monument = building_image_get_for_type(BUILDING_NATIVE_MONUMENT); int image_native_watchtower = building_image_get_for_type(BUILDING_NATIVE_WATCHTOWER); + int image_native_well = building_image_get_for_type(BUILDING_NATIVE_WELL); + int image_native_meeting_alt = building_image_get_for_type(BUILDING_NATIVE_MEETING_ALT); + int image_native_meeting_alt_2 = building_image_get_for_type(BUILDING_NATIVE_MEETING_ALT_2); + int image_native_hut_alt_2; + int image_native_palisade; + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + image_native_hut_alt_2 = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Northern_01"); + image_native_palisade = assets_get_image_id("Military", "Pal Wall N 01"); + break; + case CLIMATE_DESERT: + image_native_hut_alt_2 = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Southern_01"); + image_native_palisade = assets_get_image_id("Military", "Pal Wall S 01"); + break; + default: + image_native_hut_alt_2 = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Central_01"); + image_native_palisade = assets_get_image_id("Military", "Pal Wall C 01"); + } scenario_editor_set_native_images( image_alt_hut, @@ -194,12 +214,41 @@ int game_file_editor_write_scenario(const char *scenario_file) image_native_watchtower, image_group(GROUP_EDITOR_BUILDING_NATIVE), image_group(GROUP_EDITOR_BUILDING_NATIVE) + 2, - image_group(GROUP_EDITOR_BUILDING_CROPS) + image_group(GROUP_EDITOR_BUILDING_CROPS), + image_native_well, + image_native_meeting_alt, + image_native_hut_alt_2, + image_native_meeting_alt_2, + image_native_palisade ); scenario_distant_battle_set_roman_travel_months(); scenario_distant_battle_set_enemy_travel_months(); - if (game_file_io_write_scenario(scenario_file)) { + // Native crop variants are displayed as wheat in the editor; encode each variant + // into the per-tile image_id only for the duration of the write, then restore. + // Newly placed buildings are still in CREATED state — include both. + int crops_base = image_group(GROUP_EDITOR_BUILDING_CROPS); + for (building *b = building_first_of_type(BUILDING_NATIVE_CROPS); b; b = b->next_of_type) { + if (b->state != BUILDING_STATE_IN_USE && b->state != BUILDING_STATE_CREATED) { + continue; + } + int variant = b->subtype.orientation; + if (variant < 0 || variant > 5) { + variant = 0; + } + map_image_set(b->grid_offset, crops_base + variant * 5); + } + + int result = game_file_io_write_scenario(scenario_file); + + for (building *b = building_first_of_type(BUILDING_NATIVE_CROPS); b; b = b->next_of_type) { + if (b->state != BUILDING_STATE_IN_USE && b->state != BUILDING_STATE_CREATED) { + continue; + } + map_image_set(b->grid_offset, crops_base); + } + + if (result) { scenario_editor_set_as_saved(); return 1; } diff --git a/src/game/save_version.h b/src/game/save_version.h index 7c74e5f652..c821625209 100644 --- a/src/game/save_version.h +++ b/src/game/save_version.h @@ -44,7 +44,7 @@ If you are unsure about anything regarding the savegame versioning, please ask o typedef enum { - SAVE_GAME_CURRENT_VERSION = 0xb4, + SAVE_GAME_CURRENT_VERSION = 0xb5, SAVE_GAME_LAST_ORIGINAL_LIMITS_VERSION = 0x66, SAVE_GAME_LAST_SMALLER_IMAGE_ID_VERSION = 0x76, @@ -105,10 +105,11 @@ typedef enum { SAVE_GAME_LAST_ALTAR_ROTATION_FIX = 0xb1, SAVE_GAME_LAST_NO_HIGHWAY_STATION = 0xb2, SAVE_GAME_LAST_NO_TRIUMPHAL_ARCH_MONUMENT = 0xb3, + SAVE_GAME_LAST_NO_MORE_NATIVE_BUILDINGS = 0xb4, } savegame_version_t; typedef enum { - SCENARIO_CURRENT_VERSION = 22, + SCENARIO_CURRENT_VERSION = 23, SCENARIO_VERSION_NONE = 0, SCENARIO_LAST_UNVERSIONED = 1, @@ -131,7 +132,8 @@ typedef enum { SCENARIO_LAST_NO_VISIBLE_CUSTOM_VARIABLES = 18, SCENARIO_LAST_NO_FORMULAS_AND_MODEL_DATA = 19, SCENARIO_LAST_NO_EMPIRE_EDITOR = 20, - SCENARIO_LAST_LIMITED_ROUTE_COST = 21 + SCENARIO_LAST_LIMITED_ROUTE_COST = 21, + SCENARIO_LAST_NO_MORE_NATIVE_BUILDINGS = 22 } scenario_version_t; typedef enum { diff --git a/src/map/natives.c b/src/map/natives.c index 9a1242df41..892fabd57b 100644 --- a/src/map/natives.c +++ b/src/map/natives.c @@ -2,6 +2,7 @@ #include "assets/assets.h" #include "building/building.h" +#include "building/connectable.h" #include "building/image.h" #include "building/list.h" #include "building/properties.h" @@ -45,11 +46,16 @@ static int has_building_on_native_land(int x, int y, int size, int radius) if (type != BUILDING_MISSION_POST && type != BUILDING_NATIVE_HUT && type != BUILDING_NATIVE_HUT_ALT && + type != BUILDING_NATIVE_HUT_ALT_2 && type != BUILDING_NATIVE_MEETING && + type != BUILDING_NATIVE_MEETING_ALT && + type != BUILDING_NATIVE_MEETING_ALT_2 && type != BUILDING_NATIVE_CROPS && type != BUILDING_NATIVE_DECORATION && type != BUILDING_NATIVE_MONUMENT && type != BUILDING_NATIVE_WATCHTOWER && + type != BUILDING_NATIVE_WELL && + type != BUILDING_NATIVE_PALISADE && (!building_type_is_roadblock(type) || type == BUILDING_PALISADE_GATE || type == BUILDING_TRIUMPHAL_ARCH || type == BUILDING_GATEHOUSE || type == BUILDING_GRANARY || type == BUILDING_WAREHOUSE)) { @@ -65,7 +71,9 @@ static int has_building_on_native_land(int x, int y, int size, int radius) static void determine_meeting_center(void) { - building_type native_hut_kind[] = { BUILDING_NATIVE_HUT, BUILDING_NATIVE_HUT_ALT }; + building_type native_hut_kind[] = { BUILDING_NATIVE_HUT, BUILDING_NATIVE_HUT_ALT, BUILDING_NATIVE_HUT_ALT_2 }; + building_type meeting_kind[] = { BUILDING_NATIVE_MEETING, BUILDING_NATIVE_MEETING_ALT, + BUILDING_NATIVE_MEETING_ALT_2 }; for (int kind_idx = 0; kind_idx < sizeof(native_hut_kind) / sizeof(native_hut_kind[0]); ++kind_idx) { // Determine closest meeting center for hut @@ -75,11 +83,13 @@ static void determine_meeting_center(void) } int min_dist = 1000; int min_meeting_id = 0; - for (building *m = building_first_of_type(BUILDING_NATIVE_MEETING); m; m = m->next_of_type) { - int dist = calc_maximum_distance(b->x, b->y, m->x, m->y); - if (dist < min_dist) { - min_dist = dist; - min_meeting_id = m->id; + for (int m_idx = 0; m_idx < sizeof(meeting_kind) / sizeof(meeting_kind[0]); ++m_idx) { + for (building *m = building_first_of_type(meeting_kind[m_idx]); m; m = m->next_of_type) { + int dist = calc_maximum_distance(b->x, b->y, m->x, m->y); + if (dist < min_dist) { + min_dist = dist; + min_meeting_id = m->id; + } } } b->subtype.native_meeting_center_id = min_meeting_id; @@ -98,6 +108,17 @@ static int native_hut_alt_get_image_id(void) { } } +static int native_hut_alt_2_get_image_id(void) { + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + return assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Northern_01"); + case CLIMATE_DESERT: + return assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Southern_01"); + default: + return assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Central_01"); + } +} + void map_natives_init(void) { int image_hut = scenario.native_images.hut; @@ -107,8 +128,14 @@ void map_natives_init(void) int scenario_image_decoration = scenario.native_images.decoration; int scenario_image_monument = scenario.native_images.monument; int scenario_image_watchtower = scenario.native_images.watchtower; + int scenario_image_well = scenario.native_images.well; + int scenario_image_meeting_alt = scenario.native_images.meeting_alt; + int scenario_image_hut_alt_2 = scenario.native_images.hut_alt_2; + int scenario_image_meeting_alt_2 = scenario.native_images.meeting_alt_2; + int scenario_image_palisade = scenario.native_images.palisade; int native_image = image_group(GROUP_BUILDING_NATIVE); int native_hut_alt_image = native_hut_alt_get_image_id(); + int native_hut_alt_2_image = native_hut_alt_2_get_image_id(); int grid_offset = map_data.start_offset; for (int y = 0; y < map_data.height; y++, grid_offset += map_data.border_size) { for (int x = 0; x < map_data.width; x++, grid_offset++) { @@ -140,15 +167,43 @@ void map_natives_init(void) } else if (scenario_image_watchtower != 0 && image_id == scenario_image_watchtower) { type = BUILDING_NATIVE_WATCHTOWER; map_image_set(grid_offset, building_image_get_for_type(type)); + } else if (scenario_image_well != 0 && image_id == scenario_image_well) { + type = BUILDING_NATIVE_WELL; + map_image_set(grid_offset, building_image_get_for_type(type)); + } else if (scenario_image_hut_alt_2 != 0 && + (image_id == scenario_image_hut_alt_2 || image_id == scenario_image_hut_alt_2 + 1 || + image_id == scenario_image_hut_alt_2 + 2)) { + type = BUILDING_NATIVE_HUT_ALT_2; + map_image_set(grid_offset, native_hut_alt_2_image + (image_id - scenario_image_hut_alt_2)); + } else if (scenario_image_palisade != 0 && + image_id >= scenario_image_palisade && image_id < scenario_image_palisade + 16) { + type = BUILDING_NATIVE_PALISADE; + // Image is updated by building_connectable_update_connections later } else if (image_id == image_meeting) { type = BUILDING_NATIVE_MEETING; map_image_set(grid_offset, native_image + 2); map_image_set(grid_offset + map_grid_delta(1, 0), native_image + 2); map_image_set(grid_offset + map_grid_delta(0, 1), native_image + 2); map_image_set(grid_offset + map_grid_delta(1, 1), native_image + 2); - } else if (image_id == image_crops) { + } else if (scenario_image_meeting_alt != 0 && image_id == scenario_image_meeting_alt) { + type = BUILDING_NATIVE_MEETING_ALT; + int img = building_image_get_for_type(type); + map_image_set(grid_offset, img); + map_image_set(grid_offset + map_grid_delta(1, 0), img); + map_image_set(grid_offset + map_grid_delta(0, 1), img); + map_image_set(grid_offset + map_grid_delta(1, 1), img); + } else if (scenario_image_meeting_alt_2 != 0 && image_id == scenario_image_meeting_alt_2) { + type = BUILDING_NATIVE_MEETING_ALT_2; + int img = building_image_get_for_type(type); + map_image_set(grid_offset, img); + map_image_set(grid_offset + map_grid_delta(1, 0), img); + map_image_set(grid_offset + map_grid_delta(0, 1), img); + map_image_set(grid_offset + map_grid_delta(1, 1), img); + } else if (image_crops != 0 && image_id >= image_crops && image_id <= image_crops + 25 && + ((image_id - image_crops) % 5) == 0) { type = BUILDING_NATIVE_CROPS; - map_image_set(grid_offset, image_group(GROUP_BUILDING_FARM_CROPS) + random_bit); + int crop_offset = image_id - image_crops; + map_image_set(grid_offset, image_group(GROUP_BUILDING_FARM_CROPS) + crop_offset + random_bit); } else { //unknown building map_building_tiles_remove(0, x, y); continue; @@ -159,8 +214,11 @@ void map_natives_init(void) switch (type) { case BUILDING_NATIVE_CROPS: b->data.industry.progress = random_bit; + b->subtype.orientation = (image_id - image_crops) / 5; break; case BUILDING_NATIVE_MEETING: + case BUILDING_NATIVE_MEETING_ALT: + case BUILDING_NATIVE_MEETING_ALT_2: b->sentiment.native_anger = 100; map_building_set(grid_offset + map_grid_delta(1, 0), b->id); map_building_set(grid_offset + map_grid_delta(0, 1), b->id); @@ -169,6 +227,7 @@ void map_natives_init(void) break; case BUILDING_NATIVE_HUT: case BUILDING_NATIVE_HUT_ALT: + case BUILDING_NATIVE_HUT_ALT_2: case BUILDING_NATIVE_WATCHTOWER: b->sentiment.native_anger = 100; b->figure_spawn_delay = random_bit; @@ -181,6 +240,7 @@ void map_natives_init(void) } } + building_connectable_update_connections(); determine_meeting_center(); } @@ -191,10 +251,16 @@ void map_natives_init_editor(void) int image_crops = scenario.native_images.crops; int native_image = image_group(GROUP_EDITOR_BUILDING_NATIVE); int native_hut_alt_image = native_hut_alt_get_image_id(); + int native_hut_alt_2_image = native_hut_alt_2_get_image_id(); int scenario_image_alt_hut = scenario.native_images.alt_hut; int scenario_image_decoration = scenario.native_images.decoration; int scenario_image_monument = scenario.native_images.monument; int scenario_image_watchtower = scenario.native_images.watchtower; + int scenario_image_well = scenario.native_images.well; + int scenario_image_meeting_alt = scenario.native_images.meeting_alt; + int scenario_image_hut_alt_2 = scenario.native_images.hut_alt_2; + int scenario_image_meeting_alt_2 = scenario.native_images.meeting_alt_2; + int scenario_image_palisade = scenario.native_images.palisade; int grid_offset = map_data.start_offset; for (int y = 0; y < map_data.height; y++, grid_offset += map_data.border_size) { @@ -226,14 +292,41 @@ void map_natives_init_editor(void) } else if (scenario_image_watchtower != 0 && image_id == scenario_image_watchtower) { type = BUILDING_NATIVE_WATCHTOWER; map_image_set(grid_offset, building_image_get_for_type(type)); + } else if (scenario_image_well != 0 && image_id == scenario_image_well) { + type = BUILDING_NATIVE_WELL; + map_image_set(grid_offset, building_image_get_for_type(type)); + } else if (scenario_image_hut_alt_2 != 0 && + (image_id == scenario_image_hut_alt_2 || image_id == scenario_image_hut_alt_2 + 1 || + image_id == scenario_image_hut_alt_2 + 2)) { + type = BUILDING_NATIVE_HUT_ALT_2; + map_image_set(grid_offset, native_hut_alt_2_image + (image_id - scenario_image_hut_alt_2)); + } else if (scenario_image_palisade != 0 && + image_id >= scenario_image_palisade && image_id < scenario_image_palisade + 16) { + type = BUILDING_NATIVE_PALISADE; } else if (image_id == image_meeting) { type = BUILDING_NATIVE_MEETING; map_image_set(grid_offset, native_image + 2); map_image_set(grid_offset + map_grid_delta(1, 0), native_image + 2); map_image_set(grid_offset + map_grid_delta(0, 1), native_image + 2); map_image_set(grid_offset + map_grid_delta(1, 1), native_image + 2); - } else if (image_id == image_crops) { + } else if (scenario_image_meeting_alt != 0 && image_id == scenario_image_meeting_alt) { + type = BUILDING_NATIVE_MEETING_ALT; + int img = building_image_get_for_type(type); + map_image_set(grid_offset, img); + map_image_set(grid_offset + map_grid_delta(1, 0), img); + map_image_set(grid_offset + map_grid_delta(0, 1), img); + map_image_set(grid_offset + map_grid_delta(1, 1), img); + } else if (scenario_image_meeting_alt_2 != 0 && image_id == scenario_image_meeting_alt_2) { + type = BUILDING_NATIVE_MEETING_ALT_2; + int img = building_image_get_for_type(type); + map_image_set(grid_offset, img); + map_image_set(grid_offset + map_grid_delta(1, 0), img); + map_image_set(grid_offset + map_grid_delta(0, 1), img); + map_image_set(grid_offset + map_grid_delta(1, 1), img); + } else if (image_crops != 0 && image_id >= image_crops && image_id <= image_crops + 25 && + ((image_id - image_crops) % 5) == 0) { type = BUILDING_NATIVE_CROPS; + // Reset display to wheat (variant kept on the building) map_image_set(grid_offset, image_group(GROUP_EDITOR_BUILDING_CROPS)); } else { //unknown building map_building_tiles_remove(0, x, y); @@ -242,7 +335,12 @@ void map_natives_init_editor(void) building *b = building_create(type, x, y); b->state = BUILDING_STATE_IN_USE; map_building_set(grid_offset, b->id); - if (type == BUILDING_NATIVE_MEETING) { + if (type == BUILDING_NATIVE_CROPS) { + int crop_offset = image_id - image_crops; + b->subtype.orientation = crop_offset / 5; + } + if (type == BUILDING_NATIVE_MEETING || type == BUILDING_NATIVE_MEETING_ALT || + type == BUILDING_NATIVE_MEETING_ALT_2) { map_building_set(grid_offset + map_grid_delta(1, 0), b->id); map_building_set(grid_offset + map_grid_delta(0, 1), b->id); map_building_set(grid_offset + map_grid_delta(1, 1), b->id); @@ -252,6 +350,7 @@ void map_natives_init_editor(void) } } } + building_connectable_update_connections(); } void map_natives_check_land(int update_behavior) @@ -261,8 +360,9 @@ void map_natives_check_land(int update_behavior) city_military_decrease_native_attack_duration(); } - building_type native_buildings[] = { BUILDING_NATIVE_HUT, BUILDING_NATIVE_HUT_ALT, - BUILDING_NATIVE_MEETING, BUILDING_NATIVE_WATCHTOWER }; + building_type native_buildings[] = { BUILDING_NATIVE_HUT, BUILDING_NATIVE_HUT_ALT, BUILDING_NATIVE_HUT_ALT_2, + BUILDING_NATIVE_MEETING, BUILDING_NATIVE_MEETING_ALT, BUILDING_NATIVE_MEETING_ALT_2, + BUILDING_NATIVE_WATCHTOWER }; for (int i = 0; i < sizeof(native_buildings) / sizeof(native_buildings[0]); i++) { building_type type = native_buildings[i]; @@ -284,3 +384,4 @@ void map_natives_check_land(int update_behavior) } } } + diff --git a/src/scenario/data.h b/src/scenario/data.h index 308778c7e2..569172ad01 100644 --- a/src/scenario/data.h +++ b/src/scenario/data.h @@ -131,6 +131,11 @@ extern struct scenario_t { int hut; int meeting; int crops; + int well; + int meeting_alt; + int hut_alt_2; + int meeting_alt_2; + int palisade; } native_images; struct { // used to be stored in the settings file diff --git a/src/scenario/editor.c b/src/scenario/editor.c index 7c1c7e8cef..feb25b0f0d 100644 --- a/src/scenario/editor.c +++ b/src/scenario/editor.c @@ -121,7 +121,9 @@ void scenario_editor_create(int map_size) } void scenario_editor_set_native_images(int image_alt_hut, int image_native_decoration, - int image_native_monument, int image_native_watchtower, int image_hut, int image_meeting, int image_crops) + int image_native_monument, int image_native_watchtower, int image_hut, int image_meeting, int image_crops, + int image_native_well, int image_native_meeting_alt, int image_native_hut_alt_2, int image_native_meeting_alt_2, + int image_native_palisade) { scenario.native_images.alt_hut = image_alt_hut; scenario.native_images.decoration = image_native_decoration; @@ -130,6 +132,11 @@ void scenario_editor_set_native_images(int image_alt_hut, int image_native_decor scenario.native_images.hut = image_hut; scenario.native_images.meeting = image_meeting; scenario.native_images.crops = image_crops; + scenario.native_images.well = image_native_well; + scenario.native_images.meeting_alt = image_native_meeting_alt; + scenario.native_images.hut_alt_2 = image_native_hut_alt_2; + scenario.native_images.meeting_alt_2 = image_native_meeting_alt_2; + scenario.native_images.palisade = image_native_palisade; } void scenario_editor_cycle_image(int forward) diff --git a/src/scenario/editor.h b/src/scenario/editor.h index 9fffa52103..72c8f004aa 100644 --- a/src/scenario/editor.h +++ b/src/scenario/editor.h @@ -10,7 +10,9 @@ void scenario_editor_set_as_saved(void); void scenario_editor_set_as_unsaved(void); void scenario_editor_set_native_images(int image_alt_hut, int image_native_decoration, - int image_native_monument, int image_native_watchtower, int image_hut, int image_meeting, int image_crops); + int image_native_monument, int image_native_watchtower, int image_hut, int image_meeting, int image_crops, + int image_native_well, int image_native_meeting_alt, int image_native_hut_alt_2, int image_native_meeting_alt_2, + int image_native_palisade); void scenario_editor_cycle_image(int forward); diff --git a/src/scenario/scenario.c b/src/scenario/scenario.c index 887f42187d..5a6e84fe31 100644 --- a/src/scenario/scenario.c +++ b/src/scenario/scenario.c @@ -172,6 +172,10 @@ static void calculate_buffer_offsets(int scenario_version) next_start_offset = buffer_offsets.native_buildings + 12; } + if (scenario_version > SCENARIO_LAST_NO_MORE_NATIVE_BUILDINGS) { + next_start_offset += 20; // 5 new native image fields × 4 bytes + } + if (scenario_version > SCENARIO_LAST_NO_CUSTOM_MESSAGES) { buffer_offsets.introduction = next_start_offset; next_start_offset = buffer_offsets.introduction + 4; @@ -211,6 +215,8 @@ int scenario_get_state_buffer_size_by_savegame_version(int savegame_version) calculate_buffer_offsets(SCENARIO_LAST_NO_EXTRA_NATIVE_BUILDINGS); } else if (savegame_version <= SAVE_GAME_LAST_NO_FORMULAS_AND_MODEL_DATA) { calculate_buffer_offsets(SCENARIO_LAST_NO_FORMULAS_AND_MODEL_DATA); + } else if (savegame_version <= SAVE_GAME_LAST_NO_MORE_NATIVE_BUILDINGS) { + calculate_buffer_offsets(SCENARIO_LAST_NO_MORE_NATIVE_BUILDINGS); } else { calculate_buffer_offsets(SCENARIO_CURRENT_VERSION); } @@ -361,6 +367,11 @@ void scenario_save_state(buffer *buf) buffer_write_i32(buf, scenario.native_images.monument); buffer_write_i32(buf, scenario.native_images.watchtower); + buffer_write_i32(buf, scenario.native_images.well); + buffer_write_i32(buf, scenario.native_images.meeting_alt); + buffer_write_i32(buf, scenario.native_images.hut_alt_2); + buffer_write_i32(buf, scenario.native_images.meeting_alt_2); + buffer_write_i32(buf, scenario.native_images.palisade); buffer_write_i32(buf, scenario.intro_custom_message_id); @@ -545,6 +556,19 @@ void scenario_load_state(buffer *buf, int version) scenario.native_images.monument = buffer_read_i32(buf); scenario.native_images.watchtower = buffer_read_i32(buf); } + if (version > SCENARIO_LAST_NO_MORE_NATIVE_BUILDINGS) { + scenario.native_images.well = buffer_read_i32(buf); + scenario.native_images.meeting_alt = buffer_read_i32(buf); + scenario.native_images.hut_alt_2 = buffer_read_i32(buf); + scenario.native_images.meeting_alt_2 = buffer_read_i32(buf); + scenario.native_images.palisade = buffer_read_i32(buf); + } else { + scenario.native_images.well = 0; + scenario.native_images.meeting_alt = 0; + scenario.native_images.hut_alt_2 = 0; + scenario.native_images.meeting_alt_2 = 0; + scenario.native_images.palisade = 0; + } scenario.intro_custom_message_id = 0; if (version > SCENARIO_LAST_NO_CUSTOM_MESSAGES) { diff --git a/src/translation/english.c b/src/translation/english.c index fab49e65a5..208f891fbe 100644 --- a/src/translation/english.c +++ b/src/translation/english.c @@ -2107,6 +2107,20 @@ static translation_string all_strings[] = { {TR_CONFIG_HEADER_DESIRABILITY, "Desirability"}, {TR_CONFIG_SHOW_SHORELINE_DESIRABILITY, "Show shoreline desirability in overlay"}, {TR_CONFIG_SHOW_ELEVATION_DESIRABILITY, "Show elevation desirability in overlay"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_WELL, "Native well"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT, "Native meeting hut alt"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT_2, "Native hut alt 2"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT_2, "Native meeting hut alt 2"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_PALISADE, "Native palisade"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELDS, "Native fields"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_WHEAT, "Wheat field"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VEGETABLES, "Vegetable field"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_FRUIT, "Fruit field"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_OLIVE, "Olive field"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VINES, "Vines field"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_PIG, "Pig field"}, + {TR_BUILDING_NATIVE_WELL, "Native Well"}, + {TR_BUILDING_NATIVE_WELL_DESC, "This hand-dug well draws water from the earth to sustain the local tribe. Though primitive compared to Roman aqueducts and fountains, it suffices for the natives' modest needs. The villagers gather here at dawn and dusk to draw water, exchange news, and observe the rhythms of their ancestral land."}, {TR_BUILDING_TRIUMPHAL_ARCH_PHASE_1, "(Supports)"}, {TR_BUILDING_TRIUMPHAL_ARCH_PHASE_2, "(Facade)"}, {TR_BUILDING_TRIUMPHAL_ARCH_PHASE_1_TEXT, "Masons are laying the stonework and erecting towering pillars for a colossal triumphal arch, forged to immortalize the valor of your armies and proclaim their victories to generations yet unborn."}, diff --git a/src/translation/french.c b/src/translation/french.c index 75fcf558ac..1ddb35e581 100644 --- a/src/translation/french.c +++ b/src/translation/french.c @@ -2107,6 +2107,20 @@ static translation_string all_strings[] = { {TR_CONFIG_HEADER_DESIRABILITY, "Attrait"}, {TR_CONFIG_SHOW_SHORELINE_DESIRABILITY, "Voir l'attrait des rivages sur la carte d'attrait"}, {TR_CONFIG_SHOW_ELEVATION_DESIRABILITY, "Voir l'attrait des hauteurs sur la carte d'attrait"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_WELL, "Puits indigène"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT, "Hutte de réunion alt"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT_2, "Hutte indigène alt 2"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT_2, "Hutte de réunion alt 2"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_PALISADE, "Palissade indigène"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELDS, "Champs indigènes"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_WHEAT, "Champ de blé"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VEGETABLES, "Champ de légumes"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_FRUIT, "Champ de fruits"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_OLIVE, "Champ d'oliviers"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VINES, "Champ de vignes"}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_PIG, "Champ de porcs"}, + {TR_BUILDING_NATIVE_WELL, "Puits indigène"}, + {TR_BUILDING_NATIVE_WELL_DESC, "Ce puits creusé à la main puise l'eau de la terre pour abreuver la tribu locale. Bien que rudimentaire comparé aux aqueducs et fontaines romains, il suffit aux modestes besoins des indigènes. Les villageois s'y rassemblent à l'aube et au crépuscule pour puiser de l'eau, échanger les nouvelles, et observer les rythmes de leurs terres ancestrales."}, }; void translation_french(const translation_string **strings, int *num_strings) diff --git a/src/translation/translation.h b/src/translation/translation.h index 4742de36da..cfb28a3906 100644 --- a/src/translation/translation.h +++ b/src/translation/translation.h @@ -2102,6 +2102,20 @@ typedef enum { TR_CONFIG_HEADER_DESIRABILITY, TR_CONFIG_SHOW_SHORELINE_DESIRABILITY, TR_CONFIG_SHOW_ELEVATION_DESIRABILITY, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_WELL, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT_2, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT_2, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_PALISADE, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELDS, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_WHEAT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VEGETABLES, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_FRUIT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_OLIVE, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VINES, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_PIG, + TR_BUILDING_NATIVE_WELL, + TR_BUILDING_NATIVE_WELL_DESC, TR_BUILDING_TRIUMPHAL_ARCH_PHASE_1, TR_BUILDING_TRIUMPHAL_ARCH_PHASE_2, TR_BUILDING_TRIUMPHAL_ARCH_PHASE_1_TEXT, diff --git a/src/widget/map_editor_tool.c b/src/widget/map_editor_tool.c index 747398d573..4e36218e39 100644 --- a/src/widget/map_editor_tool.c +++ b/src/widget/map_editor_tool.c @@ -78,6 +78,8 @@ static void draw_building(const map_tile *tile, int x_view, int y_view, building } else { int image_id; if (type == BUILDING_NATIVE_CROPS) { + // Editor always shows wheat for any crop variant; in-game decoding + // uses the saved per-tile offset. image_id = image_group(GROUP_EDITOR_BUILDING_CROPS); } else if (type == BUILDING_NATIVE_HUT_ALT) { switch (scenario_property_climate()) { @@ -90,8 +92,20 @@ static void draw_building(const map_tile *tile, int x_view, int y_view, building default: image_id = assets_get_image_id("Terrain_Maps", "Native_Hut_Central_01"); }; + } else if (type == BUILDING_NATIVE_HUT_ALT_2) { + switch (scenario_property_climate()) { + case CLIMATE_NORTHERN: + image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Northern_01"); + break; + case CLIMATE_DESERT: + image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Southern_01"); + break; + default: + image_id = assets_get_image_id("Terrain_Maps", "Hellenised_Hut_Central_01"); + }; } else if (type == BUILDING_NATIVE_DECORATION || type == BUILDING_NATIVE_MONUMENT || - type == BUILDING_NATIVE_WATCHTOWER) { + type == BUILDING_NATIVE_WATCHTOWER || type == BUILDING_NATIVE_WELL || + type == BUILDING_NATIVE_MEETING_ALT || type == BUILDING_NATIVE_MEETING_ALT_2) { image_id = building_image_get_for_type(type); } else { image_id = image_group(props->image_group) + props->image_offset; @@ -252,6 +266,18 @@ void map_editor_tool_draw(const map_tile *tile) case TOOL_NATIVE_HUT_ALT: draw_building(tile, x, y, BUILDING_NATIVE_HUT_ALT); break; + case TOOL_NATIVE_HUT_ALT_2: + draw_building(tile, x, y, BUILDING_NATIVE_HUT_ALT_2); + break; + case TOOL_NATIVE_MEETING_ALT: + draw_building(tile, x, y, BUILDING_NATIVE_MEETING_ALT); + break; + case TOOL_NATIVE_MEETING_ALT_2: + draw_building(tile, x, y, BUILDING_NATIVE_MEETING_ALT_2); + break; + case TOOL_NATIVE_WELL: + draw_building(tile, x, y, BUILDING_NATIVE_WELL); + break; case TOOL_NATIVE_FIELD: draw_building(tile, x, y, BUILDING_NATIVE_CROPS); break; @@ -264,6 +290,18 @@ void map_editor_tool_draw(const map_tile *tile) case TOOL_NATIVE_WATCHTOWER: draw_building(tile, x, y, BUILDING_NATIVE_WATCHTOWER); break; + case TOOL_NATIVE_PALISADE: + if (editor_tool_is_in_use()) { + const map_tile *start_tile = editor_tool_get_start_tile(); + if (start_tile && start_tile->grid_offset) { + draw_selection_rectangle(tile, start_tile, COLOR_MASK_GREEN); + } else { + draw_flat_tile(x, y, COLOR_MASK_GREEN); + } + } else { + draw_flat_tile(x, y, COLOR_MASK_GREEN); + } + break; case TOOL_EARTHQUAKE_POINT: case TOOL_ENTRY_POINT: case TOOL_EXIT_POINT: diff --git a/src/window/building/utility.c b/src/window/building/utility.c index 5f57394f6a..7ea44f9511 100644 --- a/src/window/building/utility.c +++ b/src/window/building/utility.c @@ -504,6 +504,24 @@ void window_building_draw_native_watchtower(building_info_context *c) window_building_draw_description_at(c, 96, CUSTOM_TRANSLATION, TR_BUILDING_NATIVE_WATCHTOWER_DESC); } +void window_building_draw_native_well(building_info_context *c) +{ + c->help_id = 0; + window_building_play_sound(c, "wavs/well.wav"); + outer_panel_draw(c->x_offset, c->y_offset, c->width_blocks, c->height_blocks); + lang_text_draw_centered(CUSTOM_TRANSLATION, TR_BUILDING_NATIVE_WELL, + c->x_offset, c->y_offset + 10, BLOCK_SIZE * c->width_blocks, FONT_LARGE_BLACK); + window_building_draw_description_at(c, 96, CUSTOM_TRANSLATION, TR_BUILDING_NATIVE_WELL_DESC); +} + +void window_building_draw_native_palisade(building_info_context *c) +{ + c->help_id = 0; + outer_panel_draw(c->x_offset, c->y_offset, c->width_blocks, c->height_blocks); + lang_text_draw_centered(CUSTOM_TRANSLATION, TR_EDITOR_SCENARIO_BUILDING_NATIVE_PALISADE, + c->x_offset, c->y_offset + 10, BLOCK_SIZE * c->width_blocks, FONT_LARGE_BLACK); +} + void window_building_draw_highway(building_info_context *c) { //c->help_id = 0; diff --git a/src/window/building/utility.h b/src/window/building/utility.h index 05ac96ef1e..c07dc65181 100644 --- a/src/window/building/utility.h +++ b/src/window/building/utility.h @@ -36,6 +36,8 @@ void window_building_draw_native_crops(building_info_context *c); void window_building_draw_native_decoration(building_info_context *c); void window_building_draw_native_monument(building_info_context *c); void window_building_draw_native_watchtower(building_info_context *c); +void window_building_draw_native_well(building_info_context *c); +void window_building_draw_native_palisade(building_info_context *c); void window_building_draw_highway(building_info_context *c); diff --git a/src/window/building_info.c b/src/window/building_info.c index bbb5909d80..dc4ddd7519 100644 --- a/src/window/building_info.c +++ b/src/window/building_info.c @@ -162,11 +162,16 @@ static int get_height_id(void) case BUILDING_RESERVOIR: case BUILDING_NATIVE_HUT: case BUILDING_NATIVE_HUT_ALT: + case BUILDING_NATIVE_HUT_ALT_2: case BUILDING_NATIVE_MEETING: + case BUILDING_NATIVE_MEETING_ALT: + case BUILDING_NATIVE_MEETING_ALT_2: case BUILDING_NATIVE_CROPS: case BUILDING_NATIVE_DECORATION: case BUILDING_NATIVE_MONUMENT: case BUILDING_NATIVE_WATCHTOWER: + case BUILDING_NATIVE_WELL: + case BUILDING_NATIVE_PALISADE: case BUILDING_MISSION_POST: case BUILDING_GATEHOUSE: case BUILDING_TOWER: @@ -796,9 +801,11 @@ static void draw_background(void) window_building_draw_fort(&context); } else if (btype == BUILDING_BURNING_RUIN) { window_building_draw_burning_ruin(&context); - } else if (btype == BUILDING_NATIVE_HUT || btype == BUILDING_NATIVE_HUT_ALT) { + } else if (btype == BUILDING_NATIVE_HUT || btype == BUILDING_NATIVE_HUT_ALT || + btype == BUILDING_NATIVE_HUT_ALT_2) { window_building_draw_native_hut(&context); - } else if (btype == BUILDING_NATIVE_MEETING) { + } else if (btype == BUILDING_NATIVE_MEETING || btype == BUILDING_NATIVE_MEETING_ALT || + btype == BUILDING_NATIVE_MEETING_ALT_2) { window_building_draw_native_meeting(&context); } else if (btype == BUILDING_NATIVE_CROPS) { window_building_draw_native_crops(&context); @@ -808,6 +815,10 @@ static void draw_background(void) window_building_draw_native_monument(&context); } else if (btype == BUILDING_NATIVE_WATCHTOWER) { window_building_draw_native_watchtower(&context); + } else if (btype == BUILDING_NATIVE_WELL) { + window_building_draw_native_well(&context); + } else if (btype == BUILDING_NATIVE_PALISADE) { + window_building_draw_native_palisade(&context); } else if (btype == BUILDING_MISSION_POST) { window_building_draw_mission_post(&context); } else if (btype == BUILDING_WATCHTOWER) { diff --git a/src/window/city.c b/src/window/city.c index 4c33ec8d40..003c3d1053 100644 --- a/src/window/city.c +++ b/src/window/city.c @@ -620,7 +620,10 @@ static void show_overlay_from_grid_offset(int grid_offset) case BUILDING_MISSION_POST: case BUILDING_NATIVE_HUT: case BUILDING_NATIVE_HUT_ALT: + case BUILDING_NATIVE_HUT_ALT_2: case BUILDING_NATIVE_MEETING: + case BUILDING_NATIVE_MEETING_ALT: + case BUILDING_NATIVE_MEETING_ALT_2: overlay = OVERLAY_NATIVE; break; case BUILDING_WAREHOUSE: diff --git a/src/window/editor/build_menu.c b/src/window/editor/build_menu.c index 2e41d7d4a8..3b92b406af 100644 --- a/src/window/editor/build_menu.c +++ b/src/window/editor/build_menu.c @@ -50,11 +50,25 @@ static const int MENU_TYPES[MENU_NUM_ITEMS][MAX_ITEMS_PER_MENU] = { {7, 8, 9, -1}, {10, 11, 12, 13, 14, 15, 16, 17, -1}, {18, 19, -1}, - {20, TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT, 21, 22, - TR_EDITOR_SCENARIO_BUILDING_NATIVE_DECORATION, TR_EDITOR_SCENARIO_BUILDING_NATIVE_MONUMENT, - TR_EDITOR_SCENARIO_BUILDING_NATIVE_WATCHTOWER, TR_EDITOR_RUBBLE,-1}, + {20, TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_HUT_ALT_2, 21, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_MEETING_ALT_2, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELDS, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_DECORATION, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_MONUMENT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_WATCHTOWER, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_WELL, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_PALISADE, + TR_EDITOR_RUBBLE, -1}, {23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, -1}, {TR_EDITOR_TOOL_EARTHQUAKE_POINT, TR_EDITOR_TOOL_EARTHQUAKE_CUSTOM, TR_EDITOR_TOOL_EARTHQUAKE_REMOVE, -1}, + {TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_WHEAT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VEGETABLES, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_FRUIT, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_OLIVE, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_VINES, + TR_EDITOR_SCENARIO_BUILDING_NATIVE_FIELD_PIG, -1}, }; static struct { @@ -171,14 +185,25 @@ static void button_menu_item(const generic_button *button) switch (index) { case 0: editor_tool_set_type(TOOL_NATIVE_HUT); break; case 1: editor_tool_set_type(TOOL_NATIVE_HUT_ALT); break; - case 2: editor_tool_set_type(TOOL_NATIVE_CENTER); break; - case 3: editor_tool_set_type(TOOL_NATIVE_FIELD); break; - case 4: editor_tool_set_type(TOOL_NATIVE_DECORATION); break; - case 5: editor_tool_set_type(TOOL_NATIVE_MONUMENT); break; - case 6: editor_tool_set_type(TOOL_NATIVE_WATCHTOWER); break; - case 7: editor_tool_set_type(TOOL_NATIVE_RUINS); break; + case 2: editor_tool_set_type(TOOL_NATIVE_HUT_ALT_2); break; + case 3: editor_tool_set_type(TOOL_NATIVE_CENTER); break; + case 4: editor_tool_set_type(TOOL_NATIVE_MEETING_ALT); break; + case 5: editor_tool_set_type(TOOL_NATIVE_MEETING_ALT_2); break; + case 6: + // Open the Native Fields submenu instead of placing a building + window_editor_build_menu_show(MENU_NATIVE_FIELDS); + return; + case 7: editor_tool_set_type(TOOL_NATIVE_DECORATION); break; + case 8: editor_tool_set_type(TOOL_NATIVE_MONUMENT); break; + case 9: editor_tool_set_type(TOOL_NATIVE_WATCHTOWER); break; + case 10: editor_tool_set_type(TOOL_NATIVE_WELL); break; + case 11: editor_tool_set_type(TOOL_NATIVE_PALISADE); break; + case 12: editor_tool_set_type(TOOL_NATIVE_RUINS); break; } break; + case MENU_NATIVE_FIELDS: + editor_tool_set_with_id(TOOL_NATIVE_FIELD, index); + break; case MENU_INVASION_POINTS: editor_tool_set_with_id(TOOL_INVASION_POINT, index); break; diff --git a/src/window/editor/build_menu.h b/src/window/editor/build_menu.h index 4ef7b044ed..a123a9664c 100644 --- a/src/window/editor/build_menu.h +++ b/src/window/editor/build_menu.h @@ -11,7 +11,8 @@ enum { MENU_NATIVE_BUILDINGS = 5, MENU_ANIMAL_POINTS = 6, MENU_EARTHQUAKE = 7, - MENU_NUM_ITEMS = 8 + MENU_NATIVE_FIELDS = 8, + MENU_NUM_ITEMS = 9 }; void window_editor_build_menu_show(int submenu);