#include "AppHdr.h" #include "colour.h" #include "env.h" #include "options.h" #include "player.h" #include "random.h" unsigned char random_colour(void) { return (1 + random2(15)); } unsigned char random_uncommon_colour() { unsigned char result; do result = random_colour(); while (result == LIGHTCYAN || result == CYAN || result == BROWN); return (result); } unsigned char make_low_colour(unsigned char colour) { if (colour >= 8 && colour <= 15) return (colour - 8); return (colour); } unsigned char make_high_colour(unsigned char colour) { if (colour <= 7) return (colour + 8); return (colour); } // returns if a colour is one of the special element colours (ie not regular) bool is_element_colour( int col ) { // stripping any COLFLAGS (just in case) return ((col & 0x007f) >= ETC_FIRE); } int element_colour( int element, bool no_random ) { // Doing this so that we don't have to do recursion here at all // (these were the only cases which had possible double evaluation): if (element == ETC_FLOOR) element = env.floor_colour; else if (element == ETC_ROCK) element = env.rock_colour; // pass regular colours through for safety. if (!is_element_colour( element )) return (element); int ret = BLACK; // Setting no_random to true will get the first colour in the cases // below. This is potentially useful for calls to this function // which might want a consistent result. int tmp_rand = (no_random ? 0 : random2(120)); // Strip COLFLAGs just in case. element &= 0x007f; switch (element) { case ETC_FIRE: ret = (tmp_rand < 40) ? RED : (tmp_rand < 80) ? YELLOW : LIGHTRED; break; case ETC_ICE: ret = (tmp_rand < 40) ? LIGHTBLUE : (tmp_rand < 80) ? BLUE : WHITE; break; case ETC_EARTH: ret = (tmp_rand < 60) ? BROWN : LIGHTRED; break; case ETC_AIR: ret = (tmp_rand < 60) ? LIGHTGREY : WHITE; break; case ETC_ELECTRICITY: ret = (tmp_rand < 40) ? LIGHTCYAN : (tmp_rand < 80) ? LIGHTBLUE : CYAN; break; case ETC_POISON: ret = (tmp_rand < 60) ? LIGHTGREEN : GREEN; break; case ETC_WATER: ret = (tmp_rand < 60) ? BLUE : CYAN; break; case ETC_MAGIC: ret = (tmp_rand < 30) ? LIGHTMAGENTA : (tmp_rand < 60) ? LIGHTBLUE : (tmp_rand < 90) ? MAGENTA : BLUE; break; case ETC_MUTAGENIC: case ETC_WARP: ret = (tmp_rand < 60) ? LIGHTMAGENTA : MAGENTA; break; case ETC_ENCHANT: ret = (tmp_rand < 60) ? LIGHTBLUE : BLUE; break; case ETC_HEAL: ret = (tmp_rand < 60) ? LIGHTBLUE : YELLOW; break; case ETC_BLOOD: ret = (tmp_rand < 60) ? RED : DARKGREY; break; case ETC_DEATH: // assassin case ETC_NECRO: // necromancer ret = (tmp_rand < 80) ? DARKGREY : MAGENTA; break; case ETC_UNHOLY: // ie demonology ret = (tmp_rand < 80) ? DARKGREY : RED; break; case ETC_DARK: ret = (tmp_rand < 80) ? DARKGREY : LIGHTGREY; break; case ETC_HOLY: ret = (tmp_rand < 60) ? YELLOW : WHITE; break; case ETC_VEHUMET: ret = (tmp_rand < 40) ? LIGHTRED : (tmp_rand < 80) ? LIGHTMAGENTA : LIGHTBLUE; break; case ETC_BEOGH: ret = (tmp_rand < 60) ? LIGHTRED // plain Orc colour : BROWN; // Orcish mines wall/idol colour break; case ETC_CRYSTAL: ret = (tmp_rand < 40) ? LIGHTGREY : (tmp_rand < 80) ? GREEN : LIGHTRED; break; case ETC_SLIME: ret = (tmp_rand < 40) ? GREEN : (tmp_rand < 80) ? BROWN : LIGHTGREEN; break; case ETC_SMOKE: ret = (tmp_rand < 30) ? LIGHTGREY : (tmp_rand < 60) ? DARKGREY : (tmp_rand < 90) ? LIGHTBLUE : MAGENTA; break; case ETC_JEWEL: ret = (tmp_rand < 12) ? WHITE : (tmp_rand < 24) ? YELLOW : (tmp_rand < 36) ? LIGHTMAGENTA : (tmp_rand < 48) ? LIGHTRED : (tmp_rand < 60) ? LIGHTGREEN : (tmp_rand < 72) ? LIGHTBLUE : (tmp_rand < 84) ? MAGENTA : (tmp_rand < 96) ? RED : (tmp_rand < 108) ? GREEN : BLUE; break; case ETC_ELVEN: ret = (tmp_rand < 40) ? LIGHTGREEN : (tmp_rand < 80) ? GREEN : (tmp_rand < 100) ? LIGHTBLUE : BLUE; break; case ETC_DWARVEN: ret = (tmp_rand < 40) ? BROWN : (tmp_rand < 80) ? LIGHTRED : (tmp_rand < 100) ? LIGHTGREY : CYAN; break; case ETC_ORCISH: ret = (tmp_rand < 40) ? DARKGREY : (tmp_rand < 80) ? RED : (tmp_rand < 100) ? BROWN : MAGENTA; break; case ETC_GILA: ret = (tmp_rand < 30) ? LIGHTMAGENTA : (tmp_rand < 60) ? MAGENTA : (tmp_rand < 90) ? YELLOW : (tmp_rand < 105) ? LIGHTRED : RED; break; case ETC_KRAKEN: ret = (tmp_rand < 15) ? GREEN : (tmp_rand < 30) ? LIGHTGREEN : (tmp_rand < 45) ? LIGHTCYAN : (tmp_rand < 60) ? LIGHTBLUE : (tmp_rand < 75) ? RED : (tmp_rand < 90) ? LIGHTRED : (tmp_rand < 105) ? MAGENTA : LIGHTMAGENTA; break; case ETC_STONE: if (player_in_branch(BRANCH_HALL_OF_ZOT)) ret = env.rock_colour; else ret = LIGHTGREY; break; case ETC_MIST: ret = (tmp_rand < 100) ? CYAN : BLUE; break; case ETC_SHIMMER_BLUE: ret = (tmp_rand < 90) ? BLUE : (tmp_rand < 110) ? LIGHTBLUE : CYAN; break; case ETC_DECAY: ret = (tmp_rand < 60) ? BROWN : GREEN; break; case ETC_SILVER: ret = (tmp_rand < 90) ? LIGHTGREY : WHITE; break; case ETC_GOLD: ret = (tmp_rand < 60) ? YELLOW : BROWN; break; case ETC_IRON: ret = (tmp_rand < 40) ? CYAN : (tmp_rand < 80) ? LIGHTGREY : DARKGREY; break; case ETC_BONE: ret = (tmp_rand < 90) ? WHITE : LIGHTGREY; break; case ETC_RANDOM: ret = random_colour(); // always random break; case ETC_FLOOR: // should already be handled case ETC_ROCK: // should already be handled default: break; } ASSERT(!is_element_colour(ret)); return ((ret == BLACK) ? GREEN : ret); } #ifdef USE_TILE static std::string tile_cols[24] = { "black", "darkgrey", "grey", "lightgrey", "white", "blue", "lightblue", "darkblue", "green", "lightgreen", "darkgreen", "cyan", "lightcyan", "darkcyan", "red", "lightred", "darkred", "magenta", "lightmagenta", "darkmagenta", "yellow", "lightyellow", "darkyellow", "brown" }; unsigned int str_to_tile_colour(std::string colour) { if (colour.empty()) return (0); lowercase(colour); if (colour == "darkgray") colour = "darkgrey"; else if (colour == "gray") colour = "grey"; else if (colour == "lightgray") colour = "lightgrey"; for (unsigned int i = 0; i < 24; i++) { if (tile_cols[i] == colour) return (i); } return (0); } #endif const std::string cols[16] = { "black", "blue", "green", "cyan", "red", "magenta", "brown", "lightgrey", "darkgrey", "lightblue", "lightgreen", "lightcyan", "lightred", "lightmagenta", "yellow", "white" }; const std::string colour_to_str(unsigned char colour) { if ( colour >= 16 ) return "lightgrey"; else return cols[colour]; } // Returns -1 if unmatched else returns 0-15. int str_to_colour( const std::string &str, int default_colour, bool accept_number ) { int ret; static const std::string element_cols[] = { "fire", "ice", "earth", "electricity", "air", "poison", "water", "magic", "mutagenic", "warp", "enchant", "heal", "holy", "dark", "death", "necro", "unholy", "vehumet", "beogh", "crystal", "blood", "smoke", "slime", "jewel", "elven", "dwarven", "orcish", "gila", "kraken", "floor", "rock", "stone", "mist", "shimmer_blue", "decay", "silver", "gold", "iron", "bone", "random" }; ASSERT(ARRAYSZ(element_cols) == (ETC_RANDOM - ETC_FIRE) + 1); for (ret = 0; ret < 16; ++ret) { if (str == cols[ret]) break; } // Check for alternate spellings. if (ret == 16) { if (str == "lightgray") ret = 7; else if (str == "darkgray") ret = 8; } if (ret == 16) { // Maybe we have an element colour attribute. for (unsigned i = 0; i < sizeof(element_cols) / sizeof(*element_cols); ++i) { if (str == element_cols[i]) { // Ugh. ret = element_type(ETC_FIRE + i); break; } } } if (ret == 16 && accept_number) { // Check if we have a direct colour index. const char *s = str.c_str(); char *es = NULL; const int ci = static_cast(strtol(s, &es, 10)); if (s != (const char *) es && es && ci >= 0 && ci < 16) ret = ci; } return ((ret == 16) ? default_colour : ret); } #if defined(TARGET_OS_WINDOWS) || defined(TARGET_OS_DOS) || defined(USE_TILE) static unsigned short _dos_reverse_brand(unsigned short colour) { if (Options.dos_use_background_intensity) { // If the console treats the intensity bit on background colours // correctly, we can do a very simple colour invert. // Special casery for shadows. if (colour == BLACK) colour = (DARKGREY << 4); else colour = (colour & 0xF) << 4; } else { // If we're on a console that takes its DOSness very seriously the // background high-intensity bit is actually a blink bit. Blinking is // evil, so we strip the background high-intensity bit. This, sadly, // limits us to 7 background colours. // Strip off high-intensity bit. Special case DARKGREY, since it's the // high-intensity counterpart of black, and we don't want black on // black. // // We *could* set the foreground colour to WHITE if the background // intensity bit is set, but I think we've carried the // angry-fruit-salad theme far enough already. if (colour == DARKGREY) colour |= (LIGHTGREY << 4); else if (colour == BLACK) colour = LIGHTGREY << 4; else { // Zap out any existing background colour, and the high // intensity bit. colour &= 7; // And swap the foreground colour over to the background // colour, leaving the foreground black. colour <<= 4; } } return (colour); } static unsigned short _dos_hilite_brand(unsigned short colour, unsigned short hilite) { if (!hilite) return (colour); if (colour == hilite) colour = 0; colour |= (hilite << 4); return (colour); } unsigned short dos_brand( unsigned short colour, unsigned brand) { if ((brand & CHATTR_ATTRMASK) == CHATTR_NORMAL) return (colour); colour &= 0xFF; if ((brand & CHATTR_ATTRMASK) == CHATTR_HILITE) return _dos_hilite_brand(colour, (brand & CHATTR_COLMASK) >> 8); else return _dos_reverse_brand(colour); } #endif #if defined(TARGET_OS_WINDOWS) || defined(TARGET_OS_DOS) || defined(USE_TILE) static unsigned _colflag2brand(int colflag) { switch (colflag) { case COLFLAG_ITEM_HEAP: return (Options.heap_brand); case COLFLAG_FRIENDLY_MONSTER: return (Options.friend_brand); case COLFLAG_NEUTRAL_MONSTER: return (Options.neutral_brand); case COLFLAG_WILLSTAB: return (Options.stab_brand); case COLFLAG_MAYSTAB: return (Options.may_stab_brand); case COLFLAG_FEATURE_ITEM: return (Options.feature_item_brand); case COLFLAG_TRAP_ITEM: return (Options.trap_item_brand); default: return (CHATTR_NORMAL); } } #endif unsigned real_colour(unsigned raw_colour) { // This order is important - is_element_colour() doesn't want to see the // munged colours returned by dos_brand, so it should always be done // before applying DOS brands. const int colflags = raw_colour & 0xFF00; // Evaluate any elemental colours to guarantee vanilla colour is returned if (is_element_colour( raw_colour )) raw_colour = colflags | element_colour( raw_colour ); #if defined(TARGET_OS_WINDOWS) || defined(TARGET_OS_DOS) || defined(USE_TILE) if (colflags) { unsigned brand = _colflag2brand(colflags); raw_colour = dos_brand(raw_colour & 0xFF, brand); } #endif #ifndef USE_COLOUR_OPTS // Strip COLFLAGs for systems that can't do anything meaningful with them. raw_colour &= 0xFF; #endif return (raw_colour); }