diff --git a/assets/img/new/ovr_race_gordoon.png b/assets/img/new/ovr_race_gordoon.png new file mode 100644 index 00000000..60ac4ac7 Binary files /dev/null and b/assets/img/new/ovr_race_gordoon.png differ diff --git a/assets/img/new/ovr_race_human.png b/assets/img/new/ovr_race_human.png new file mode 100644 index 00000000..d9f0a18c Binary files /dev/null and b/assets/img/new/ovr_race_human.png differ diff --git a/assets/img/new/ovr_race_native.png b/assets/img/new/ovr_race_native.png new file mode 100644 index 00000000..d43ea97c Binary files /dev/null and b/assets/img/new/ovr_race_native.png differ diff --git a/assets/img/new/ovr_race_teri.png b/assets/img/new/ovr_race_teri.png new file mode 100644 index 00000000..c1fd7068 Binary files /dev/null and b/assets/img/new/ovr_race_teri.png differ diff --git a/assets/img/new/ovr_race_urkash.png b/assets/img/new/ovr_race_urkash.png new file mode 100644 index 00000000..307136f5 Binary files /dev/null and b/assets/img/new/ovr_race_urkash.png differ diff --git a/assets/img/new/ovr_race_yokon.png b/assets/img/new/ovr_race_yokon.png new file mode 100644 index 00000000..80c64fc4 Binary files /dev/null and b/assets/img/new/ovr_race_yokon.png differ diff --git a/src/assetpaths.cpp b/src/assetpaths.cpp index 2fc2391c..566bef5a 100644 --- a/src/assetpaths.cpp +++ b/src/assetpaths.cpp @@ -1396,6 +1396,12 @@ const char *IMG_NI_SHUTTLE4 = D_NEW "SH4_shuttle4"; const char *IMG_NI_CR1 = D_NEW "cr1"; const char *IMG_NI_DR1 = D_NEW "dr1"; const char *IMG_NI_GO1 = D_NEW "go1"; +const char *IMG_OVR_RACE_HUMAN = D_NEW "ovr_race_human"; //JK: Feature EF_CITY_RACES +const char *IMG_OVR_RACE_TERI = D_NEW "ovr_race_teri"; //JK: Feature EF_CITY_RACES +const char *IMG_OVR_RACE_URKASH = D_NEW "ovr_race_urkash"; //JK: Feature EF_CITY_RACES +const char *IMG_OVR_RACE_GORDOON = D_NEW "ovr_race_gordoon"; //JK: Feature EF_CITY_RACES +const char *IMG_OVR_RACE_YOKON = D_NEW "ovr_race_yokon"; //JK: Feature EF_CITY_RACES +const char *IMG_OVR_RACE_NATIVE = D_NEW "ovr_race_native"; //JK: Feature EF_CITY_RACES const char *IMG_OVR_NO_POWER = D_NEW "ovr_no_power"; //JK: Feature EF_IMPROVED_PLANET_MAP_UX // Special cases @@ -2790,6 +2796,12 @@ const char *ASSETS_IMG[] = { IMG_NI_CR1, IMG_NI_DR1, IMG_NI_GO1, + IMG_OVR_RACE_HUMAN, //JK: Feature EF_CITY_RACES + IMG_OVR_RACE_TERI, //JK: Feature EF_CITY_RACES + IMG_OVR_RACE_URKASH, //JK: Feature EF_CITY_RACES + IMG_OVR_RACE_GORDOON, //JK: Feature EF_CITY_RACES + IMG_OVR_RACE_YOKON, //JK: Feature EF_CITY_RACES + IMG_OVR_RACE_NATIVE, //JK: Feature EF_CITY_RACES IMG_OVR_NO_POWER, //JK: Feature EF_IMPROVED_PLANET_MAP_UX "", diff --git a/src/assetpaths.h b/src/assetpaths.h index 1424d846..9777b3fe 100644 --- a/src/assetpaths.h +++ b/src/assetpaths.h @@ -1401,6 +1401,12 @@ extern const char *ASSETS_SFX[]; extern const char *ASSETS_MUS[]; extern const char *ASSETS_IMG[]; +extern const char *IMG_OVR_RACE_HUMAN; //JK: Feature EF_CITY_RACES +extern const char *IMG_OVR_RACE_TERI; //JK: Feature EF_CITY_RACES +extern const char *IMG_OVR_RACE_URKASH; //JK: Feature EF_CITY_RACES +extern const char *IMG_OVR_RACE_GORDOON; //JK: Feature EF_CITY_RACES +extern const char *IMG_OVR_RACE_YOKON; //JK: Feature EF_CITY_RACES +extern const char *IMG_OVR_RACE_NATIVE; //JK: Feature EF_CITY_RACES extern const char *IMG_OVR_NO_POWER; //JK: Feature EF_IMPROVED_PLANET_MAP_UX #endif diff --git a/src/exodus_features.cpp b/src/exodus_features.cpp index 3fc44d5c..0a0850d6 100644 --- a/src/exodus_features.cpp +++ b/src/exodus_features.cpp @@ -186,6 +186,8 @@ static bool IMPROVED_REPORT_UX = false; //JK: Improve UX of planet surface map static bool IMPROVED_PLANET_MAP_UX = false; +//JK: Store and display the race of a city +static bool CITY_RACES = false; static bool DISABLE_MULTIPLAYER = true; @@ -255,6 +257,7 @@ void exodus_set_game_mode(ExodusGameMode game_mode) { FIX_LUNAR_BATTLE_STALEMATE = true; FIX_LUNAR_BATTLE_SOFTLOCKS = true; FIX_ATTACKPAYOFF_MC = false; + CITY_RACES = false; // JK: Feature EF_CITY_RACES ACCEPT_REBEL_PLANET = false; // JK: Feature EF_ACCEPT_REBEL_PLANET IMPROVED_REPORT_UX = false; // JK: Feature EF_IMPROVED_REPORT_UX IMPROVED_PLANET_MAP_UX = false; // JK: Feature EF_IMPROVED_PLANET_MAP_UX @@ -320,6 +323,7 @@ void exodus_set_game_mode(ExodusGameMode game_mode) { FIX_LUNAR_BATTLE_STALEMATE = true; FIX_LUNAR_BATTLE_SOFTLOCKS = true; FIX_ATTACKPAYOFF_MC = true; + CITY_RACES = true; // JK: Feature EF_CITY_RACES ACCEPT_REBEL_PLANET = true; // JK: Feature EF_ACCEPT_REBEL_PLANET IMPROVED_REPORT_UX = true; // JK: Feature EF_IMPROVED_REPORT_UX IMPROVED_PLANET_MAP_UX = true; // JK: Feature EF_IMPROVED_PLANET_MAP_UX @@ -398,6 +402,7 @@ bool FEATURE(ExodusFeature f) { if (f == EF_FIX_LUNAR_BATTLE_STALEMATE) return FIX_LUNAR_BATTLE_STALEMATE; if (f == EF_FIX_LUNAR_BATTLE_SOFTLOCKS) return FIX_LUNAR_BATTLE_SOFTLOCKS; if (f == EF_FIX_ATTACKPAYOFF_MC) return FIX_ATTACKPAYOFF_MC; + if (f == EF_CITY_RACES) return CITY_RACES; //JK: Feature EF_CITY_RACES if (f == EF_ACCEPT_REBEL_PLANET) return ACCEPT_REBEL_PLANET; //JK: Feature EF_ACCEPT_REBEL_PLANET if (f == EF_IMPROVED_REPORT_UX) return IMPROVED_REPORT_UX; //JK: Feature EF_IMPROVED_REPORT_UX if (f == EF_IMPROVED_PLANET_MAP_UX) return IMPROVED_PLANET_MAP_UX; //JK: Feature EF_IMPROVED_PLANET_MAP_UX diff --git a/src/exodus_features.h b/src/exodus_features.h index 63d1594e..fd18655e 100644 --- a/src/exodus_features.h +++ b/src/exodus_features.h @@ -66,6 +66,7 @@ enum ExodusFeature { EF_FIX_LUNAR_BATTLE_SOFTLOCKS, EF_FIX_ATTACKPAYOFF_MC, EF_DISABLE_MULTIPLAYER, + EF_CITY_RACES, // JK: Feature EF_CITY_RACES EF_ACCEPT_REBEL_PLANET, // JK: Feature EF_ACCEPT_REBEL_PLANET EF_IMPROVED_REPORT_UX, // JK: Feature EF_IMPROVED_REPORT_UX EF_IMPROVED_PLANET_MAP_UX, // JK: Feature EF_IMPROVED_PLANET_MAP_UX diff --git a/src/galaxy/planet.cpp b/src/galaxy/planet.cpp index 248d68d2..26583e0f 100644 --- a/src/galaxy/planet.cpp +++ b/src/galaxy/planet.cpp @@ -14,6 +14,8 @@ #include "util/iter.h" #include "util/value.h" +//JK: Feature EF_CITY_RACES +#include "player.h" PlanetSpriteSet sprite_sets[7]; @@ -640,6 +642,19 @@ Stone Planet::get_stone(int x, int y) { void Planet::set_stone(int x, int y, Stone stone) { int real_x; int real_y; if (_to_real(x, y, real_x, real_y)) { + + //JK: Feature EF_CITY_RACES + //Set race of city + if (stone == STONE_City) { + Race city_race; + if (get_owner() != -1) { + city_race = exostate().get_player(get_owner())->get_race(); + } else { + city_race = get_majority_race(); // fallback if planet is ownerless (e.g. republic doing academy steal) + } + set_property(x,y, PROPERTY_Race, city_race); + } + _set_stone(real_x, real_y, stone); } } @@ -658,6 +673,15 @@ void Planet::set_stone_wrap(int x, int y, Stone stone) { } } +//JK: Feature EF_CITY_RACES +//Set city + race +void Planet::set_city_wrap(int x, int y, Race race) { + if (wrap(x, y)) { + set_stone(x, y, STONE_City); + set_property(x,y, PROPERTY_Race, race); + } +} + bool Planet::wrap(int& x, int& y) { int size = get_size_blocks(); while (x < 0) x += size; @@ -1108,6 +1132,92 @@ Stone Planet::_get_stone(int x, int y) { return surf[y*PLANET_BLOCKS_LG + x]; } +//JK: Feature EF_CITY_RACES +Race Planet::get_majority_race(){ + int RACES_MAX = 16; // must correspond with bit mask of surf_property for races and placed elsewhere + Race majority = RACE_Human; //TODO: JK: We need RACE_None also, and put it here. + int race_count[RACES_MAX]; + + for (int i=0; i maj_num) { + maj_num =race_count[i]; + maj = i; + } + } + + // add: return "none" if maj_num == 0. + + // here just copying the enum order + if (maj==0) majority = RACE_Human; + if (maj==1) majority = RACE_Yokon; + if (maj==2) majority = RACE_Teri; + if (maj==3) majority = RACE_Urkash; + if (maj==4) majority = RACE_Gordoon; + + return (majority); +} + +//JK: Feature EF_CITY_RACES and potential future properties +int Planet::get_property(int x, int y, Property property) { + + int real_x; int real_y; + if (_to_real(x, y, real_x, real_y)) { + + //TODO: JK: Replace with bitwise evaluation when more than 1 property stored in surf_property + int read_value =surf_property[real_y*PLANET_BLOCKS_LG + real_x]; + + return (read_value); + } + + return(false); +} + +//JK: Feature EF_CITY_RACES and potential future properties +void Planet::set_property(int x, int y, Property property, int value) { + + int PROPERTY_Race_Limit = 15; // TODO: Move to planet.h + int real_x; int real_y; + if (_to_real(x, y, real_x, real_y)) { + + //TODO: JK: Use bitwise evaluation when more than 1 property stored in surf_property + + /* Working version with only 1 property */ + int s = value; + + /* Probably working version with >1 properties (requires clearing of surf_property on planet generation) + int s = surf_property[real_y*PLANET_BLOCKS_LG + real_x]; + switch(property) { + case PROPERTY_Race: + if (value <= PROPERTY_Race_Limit) { + uint32_t mask = ~(uint32_t)15; + s = s & mask; + s = s | value; + } + break; + } + */ + + surf_property[real_y*PLANET_BLOCKS_LG + real_x] = s; + } +} + void Planet::_set_stone(int x, int y, Stone stone) { surf[y*PLANET_BLOCKS_LG + x] = stone; // Ensure that changes to number of cities etc are @@ -2078,19 +2188,23 @@ void Planet::disown(PlanetOwnerChangedReason reason) { L.debug("%s: DISOWN: Unrest reset to 0", name); } -bool Planet::expand_city() { - return expand(STONE_City, true); +//JK: Feature EF_CITY_RACES added parameters +bool Planet::expand_city(bool override_local_race, Race race) { + return expand(STONE_City, true, override_local_race, race); } +//JK: Feature EF_CITY_RACES added parameters bool Planet::expand_city_possible() { - return expand(STONE_City, false); + return expand(STONE_City, false, false, (Race)0); } +//JK: Feature EF_CITY_RACES added parameters bool Planet::expand_village() { - return expand(STONE_Village, true); + return expand(STONE_Village, true, false, (Race)0); } -bool Planet::expand(Stone st, bool do_place) { +//JK: Feature EF_CITY_RACES added parameters +bool Planet::expand(Stone st, bool do_place, bool override_local_race, Race overriding_race) { L.info("Expanding %d on %s", st, get_name()); // Find expansion candidates @@ -2150,8 +2264,17 @@ bool Planet::expand(Stone st, bool do_place) { } if (tgt_ok) { if (do_place) { - set_stone_wrap(i + i_off, j + j_off, st); - L.info("Expansion %d,%d", i, j); + if(st == STONE_City){ + //JK: Feature EF_CITY_RACES Get origin city's race: + int new_city_race = get_property(i, j, PROPERTY_Race); + //JK: If city expands due to Academy steal, set race of immigrants: + if (override_local_race) new_city_race = overriding_race; + set_city_wrap(i + i_off, j + j_off, (Race)new_city_race); + L.info("City Expansion %d,%d", i, j); + } else { + set_stone_wrap(i + i_off, j + j_off, st); + L.info("Expansion %d,%d", i, j); + } } return true; } @@ -2168,8 +2291,8 @@ bool Planet::expand(Stone st, bool do_place) { return false; } -bool Planet::do_academy_immigration(const char* source_name) { - if (!expand_city()) { +bool Planet::do_academy_immigration(const char* source_name, Race race) { + if (!expand_city(true, race)) { //JK: Feature EF_CITY_RACES added parameters // Caller should verify expansion possible first L.error("Academy immigration called when city cannot expand"); return false; @@ -2969,6 +3092,7 @@ void Planet::save(cJSON* j) const { SAVE_NUM(j, failed_attacks_this_month); SAVE_NUM(j, bombings_this_month); SAVE_BOOL(j, processing_in_progress); + SAVE_ARRAY_OF_NUM(j, surf_property); //JK: Feature EF_CITY_RACES and potential future properties } void Planet::load(cJSON* j) { @@ -3016,4 +3140,5 @@ void Planet::load(cJSON* j) { LOAD_NUM(j, failed_attacks_this_month); LOAD_NUM(j, bombings_this_month); LOAD_BOOL(j, processing_in_progress); + LOAD_ARRAY_OF_NUM(j, surf_property); //JK: Feature EF_CITY_RACES and potential future properties } diff --git a/src/galaxy/planet.h b/src/galaxy/planet.h index aa5e1d8e..da4733a8 100644 --- a/src/galaxy/planet.h +++ b/src/galaxy/planet.h @@ -5,6 +5,9 @@ #include "save/saveable.h" +//JK: Feature EF_CITY_RACES: Include for Race enum: +#include "player.h" + #define PLANET_MAX_NAME 12 #define PLANET_BLOCKS_SM 12 #define PLANET_BLOCKS_MD 14 @@ -78,6 +81,11 @@ enum Stone : uint8_t { STONE_END = 32, }; +// JK: Feature EF_CITY_RACES (and future property-based features) +enum Property { + PROPERTY_Race +}; + class StoneSet { public: StoneSet() {reset();} @@ -341,10 +349,10 @@ class Planet : public Saveable { int get_reserves_plu(); int get_total_reserves(); void disown(PlanetOwnerChangedReason); - bool expand_city(); + bool expand_city(bool, Race); //JK: Feature EF_CITY_RACES bool expand_village(); bool expand_city_possible(); - bool do_academy_immigration(const char*); + bool do_academy_immigration(const char*, Race); //JK: Feature EF_CITY_RACES bool agri_collapse(); void monthly_processing_start(); bool monthly_processing_in_progress(); @@ -357,10 +365,15 @@ class Planet : public Saveable { int get_n_academy_sources(); const char* get_academy_source(int); + + int get_property(int x, int y, Property property); //JK: Feature EF_CITY_RACES + future Properties + void set_property(int x, int y, Property property, int value); //JK: Feature EF_CITY_RACES + future Properties + Race get_majority_race(); //JK: Feature EF_CITY_RACES + future Properties + void set_city_wrap(int x, int y, Race race); //JK: Feature EF_CITY_RACES + future Properties private: void init(); - bool expand(Stone, bool); + bool expand(Stone, bool, bool, Race); //JK: Feature EF_CITY_RACES TradeReport do_monthly_trade(Stone); @@ -382,6 +395,11 @@ class Planet : public Saveable { MoonClass moon_cls; // Determines battle background. Orig was random each time. char name[PLANET_MAX_NAME + 1]; Stone surf[MAX_STONES]; + uint32_t surf_property[MAX_STONES]; //JK: Feature EF_CITY_RACES and more potential properties later. + /* JK: Property implemented: + * bits 1-4 (0-15) Race, used by EF_CITY_RACES + * Further properties can be flora, fauna, regional bonuses. + */ int construction_phase; int star_target; // Orig: ptarget diff --git a/src/mode/galaxy_map.cpp b/src/mode/galaxy_map.cpp index b6ccb25f..11a0b088 100644 --- a/src/mode/galaxy_map.cpp +++ b/src/mode/galaxy_map.cpp @@ -4868,6 +4868,10 @@ ExodusMode GalaxyMap::month_pass_planet_update() { continue; if (!pl->get_location().has_visited(star_idx)) continue; + if (FEATURE(EF_CITY_RACES)) { //JK: Feature EF_CITY_RACES + if ( (p->get_majority_race() != pl->get_race()) && (!onein(5)) ) + continue; + } new_owner_idx = idx; new_owner = pl; break; @@ -5349,14 +5353,14 @@ ExodusMode GalaxyMap::month_pass_planet_update() { for (int i = 0; i < p->count_stones(STONE_City); ++i) { if (onein(20)) { if (Planet *thief = attempt_academy_expansion(p)) { - if (thief->do_academy_immigration(p->get_name())) { + if (thief->do_academy_immigration(p->get_name(), p->get_majority_race())) { //JK: Feature EF_CITY_RACES added parameters report.register_minor_problem(); report.add_line("Emigrants left to study at %s.", thief->get_name()); } else { L.error("attempt_academy_expansion should only return viable planets"); } } else { - if (p->expand_city()) { + if (p->expand_city(false, (Race)0)) { //JK: Feature EF_CITY_RACES added parameters report.register_good_news(); report.add_line("A city has expanded."); if (owner && owner->is_human()) { diff --git a/src/mode/lunar_battle_prep.cpp b/src/mode/lunar_battle_prep.cpp index 62606849..1ece29fb 100644 --- a/src/mode/lunar_battle_prep.cpp +++ b/src/mode/lunar_battle_prep.cpp @@ -910,6 +910,24 @@ ExodusMode LunarBattlePrep::update(float delta) { if (i > cap) i += agg->transfer_inf(i - cap); p->clear_army(); p->adjust_army(i, g, a); + + //JK: Feature EF_CITY_RACES + if (FEATURE(EF_CITY_RACES)) { + // if they're not close to rioting, they react to conqueror's race: + if (p->get_unrest() < 7 ) { + if (p->get_majority_race() != exostate().get_player(b.aggressor_idx)->get_race()) { + L.info("Taken over planet of different race majority -> less happy"); + p->adjust_unrest(3); + } else { + L.info("Taken over planet of same race majority -> more happy"); + p->adjust_unrest(-3); + } + } else { + // if close to rioting, they wait what new ruler will bring: + p->adjust_unrest(-1); + } + } + } else { NewsItem& news = exostate().register_news(NI_FailedTakeover); news.player_0 = b.aggressor_idx; diff --git a/src/mode/planet_map.cpp b/src/mode/planet_map.cpp index ce805559..1e1e8232 100644 --- a/src/mode/planet_map.cpp +++ b/src/mode/planet_map.cpp @@ -1549,6 +1549,34 @@ void PlanetMap::draw_properties() { } } + if ((active_tool == TOOL_City) && (FEATURE(EF_CITY_RACES))) { + //show races on top of all cities + if (st == STONE_City) { + const char* sprite = IMG_OVR_RACE_HUMAN; + int race = planet->get_property(x, y, PROPERTY_Race); + if (race==0) sprite = IMG_OVR_RACE_HUMAN; + if (race==1) sprite = IMG_OVR_RACE_YOKON; + if (race==2) sprite = IMG_OVR_RACE_TERI; + if (race==3) sprite = IMG_OVR_RACE_URKASH; + if (race==4) sprite = IMG_OVR_RACE_GORDOON; + + draw_manager.draw( + id_overlays[y*blocks+x], + sprite, + {surf_x + x*STONE_SZ, surf_y + y*STONE_SZ, + 0, 0, 1, 1}); + overlay_drawn = true; + } + if (st == STONE_Village) { + draw_manager.draw( + id_overlays[y*blocks+x], + IMG_OVR_RACE_NATIVE, + {surf_x + x*STONE_SZ, surf_y + y*STONE_SZ, + 0, 0, 1, 1}); + overlay_drawn = true; + } + } + if (!overlay_drawn) { draw_manager.draw(id_overlays[y*blocks+x], nullptr); } diff --git a/src/mode/planet_status.cpp b/src/mode/planet_status.cpp index 8cd264c4..3ca1e860 100644 --- a/src/mode/planet_status.cpp +++ b/src/mode/planet_status.cpp @@ -79,7 +79,28 @@ void PlanetStatus::enter() { owner ? "" : cost); char pop[31]; - snprintf(pop, 30, "Population: %d million.", p->get_population()); + + //JK: Feature EF_CITY_RACES + //TODO: JK: Replace the switch case with one method for getting a race string. + char race[20]; + int n_cities = p->get_n_cities(); + if ((FEATURE(EF_CITY_RACES)) && (n_cities > 0)) { + switch (p->get_majority_race()) { + case RACE_Human: + snprintf(race, 20, "Human"); break; + case RACE_Yokon: + snprintf(race, 20, "Yo' kon"); break; + case RACE_Teri: + snprintf(race, 20, "Teri"); break; + case RACE_Urkash: + snprintf(race, 20, "Urkash"); break; + case RACE_Gordoon: + snprintf(race, 20, "Gordoon"); break; + } + snprintf(pop, 50, "Population: %d million (%s majority)", p->get_population(), race); + } else { + snprintf(pop, 50, "Population: %d million", p->get_population()); + } char day[51]; snprintf(day, 50, "One day is %d standard hours long.", p->get_day_hours()); @@ -106,7 +127,7 @@ void PlanetStatus::enter() { } char cities[5]; - int n_cities = p->get_n_cities(); + n_cities = p->get_n_cities(); if (n_cities == 0) { strncpy(cities, "None", 5); } else {