GameActionResult::Ptr Execute() const override { rct_banner* banner = &gBanners[_bannerIndex]; utf8 *buffer = gCommonStringFormatBuffer; utf8 *dst = buffer; dst = utf8_write_codepoint(dst, FORMAT_COLOUR_CODE_START + banner->text_colour); String::Set(dst, sizeof(gCommonStringFormatBuffer) - (dst - buffer), _name.c_str(), _name.size()); rct_string_id string_id = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); if (string_id == 0) { return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_ERR_CANT_SET_BANNER_TEXT); } rct_string_id prev_string_id = banner->string_idx; banner->string_idx = string_id; user_string_free(prev_string_id); auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); intent.putExtra(INTENT_EXTRA_BANNER_INDEX, _bannerIndex); context_broadcast_intent(&intent); return MakeResult(); }
GameActionResult::Ptr Query() const override { if (_bannerIndex >= MAX_BANNERS || _bannerIndex < 0) { log_warning("Invalid game command for setting banner name, banner id = %d", _bannerIndex); return MakeResult(GA_ERROR::INVALID_PARAMETERS, STR_NONE); } // Ensure user string space. rct_string_id string_id = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, _name.c_str()); if (string_id != 0) { user_string_free(string_id); } else { return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_ERR_CANT_SET_BANNER_TEXT); } return MakeResult(); }
void window_player_open(uint8 id) { rct_window* window; int player = network_get_player_index(id); window = window_bring_to_front_by_number(WC_PLAYER, id); if (window == NULL) { window = window_create_auto_pos(240, 170, &window_player_overview_events, WC_PLAYER, WF_RESIZABLE); window->number = id; window->page = 0; window->viewport_focus_coordinates.y = 0; window->frame_no = 0; window->list_information_type = 0; window->picked_peep_frame = 0; window->highlighted_item = 0; window->min_width = 210; window->min_height = 134; window->max_width = 500; window->max_height = 450; window->no_list_items = 0; window->selected_list_item = -1; window->viewport_focus_coordinates.y = -1; window->error.var_480 = user_string_allocate(128, network_get_player_name(player)); // repurposing var_480 to store this } window->page = 0; window_invalidate(window); window->widgets = window_player_page_widgets[WINDOW_PLAYER_PAGE_OVERVIEW]; window->enabled_widgets = window_player_page_enabled_widgets[WINDOW_PLAYER_PAGE_OVERVIEW]; window->hold_down_widgets = 0; window->event_handlers = window_player_page_events[WINDOW_PLAYER_PAGE_OVERVIEW]; window->pressed_widgets = 0; window_init_scroll_widgets(window); window_player_set_page(window, WINDOW_PLAYER_PAGE_OVERVIEW); }
void fix_duplicated_banners() { // For each banner in the map, check if the banner index is in use already, and if so, create a new entry for it bool activeBanners[Util::CountOf(gBanners)]{}; rct_tile_element * tileElement; for (int y = 0; y < MAXIMUM_MAP_SIZE_TECHNICAL; y++) { for (int x = 0; x < MAXIMUM_MAP_SIZE_TECHNICAL; x++) { tileElement = map_get_first_element_at(x, y); do { // TODO: Handle walls and large-scenery that use banner indices too. Large scenery can be tricky, as they occupy // multiple tiles that should both refer to the same banner index. if (tile_element_get_type(tileElement) == TILE_ELEMENT_TYPE_BANNER) { uint8 bannerIndex = tileElement->properties.banner.index; if (activeBanners[bannerIndex]) { log_info( "Duplicated banner with index %d found at x = %d, y = %d and z = %d.", bannerIndex, x, y, tileElement->base_height); // Banner index is already in use by another banner, so duplicate it uint8 newBannerIndex = create_new_banner(GAME_COMMAND_FLAG_APPLY); if (newBannerIndex == BANNER_NULL) { log_error("Failed to create new banner."); continue; } Guard::Assert(activeBanners[newBannerIndex] == false); // Copy over the original banner, but update the location rct_banner & newBanner = gBanners[newBannerIndex]; newBanner = gBanners[bannerIndex]; newBanner.x = x; newBanner.y = y; // Duplicate user string too rct_string_id stringIdx = newBanner.string_idx; if (is_user_string_id(stringIdx)) { utf8 buffer[USER_STRING_MAX_LENGTH]; format_string(buffer, USER_STRING_MAX_LENGTH, stringIdx, nullptr); rct_string_id newStringIdx = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); if (newStringIdx == 0) { log_error("Failed to allocate user string for banner"); continue; } newBanner.string_idx = newStringIdx; } tileElement->properties.banner.index = newBannerIndex; } // Mark banner index as in-use activeBanners[bannerIndex] = true; } } while (!tile_element_is_last_for_tile(tileElement++)); } } }
static money32 BannerSetStyle(uint8 bannerIndex, uint8 colour, uint8 textColour, uint8 bannerFlags, uint8 flags) { if (bannerIndex >= MAX_BANNERS) { gGameCommandErrorText = STR_INVALID_SELECTION_OF_OBJECTS; return MONEY32_UNDEFINED; } rct_banner* banner = &gBanners[bannerIndex]; rct_tile_element* tileElement = banner_get_tile_element(bannerIndex); if (tileElement == nullptr) { return MONEY32_UNDEFINED; } if (!(flags & GAME_COMMAND_FLAG_APPLY)) { return 0; } banner->colour = colour; banner->text_colour = textColour; banner->flags = bannerFlags; tileElement->properties.banner.flags = 0xFF; if (banner->flags & BANNER_FLAG_NO_ENTRY) { tileElement->properties.banner.flags &= ~(1 << tileElement->properties.banner.position); } sint32 colourCodepoint = FORMAT_COLOUR_CODE_START + banner->text_colour; utf8 buffer[256]; format_string(buffer, 256, banner->string_idx, nullptr); sint32 firstCodepoint = utf8_get_next(buffer, nullptr); if (firstCodepoint >= FORMAT_COLOUR_CODE_START && firstCodepoint <= FORMAT_COLOUR_CODE_END) { utf8_write_codepoint(buffer, colourCodepoint); } else { utf8_insert_codepoint(buffer, colourCodepoint); } rct_string_id stringId = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); if (stringId != 0) { rct_string_id prevStringId = banner->string_idx; banner->string_idx = stringId; user_string_free(prevStringId); auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); intent.putExtra(INTENT_EXTRA_BANNER_INDEX, bannerIndex); context_broadcast_intent(&intent); } else { gGameCommandErrorText = STR_ERR_CANT_SET_BANNER_TEXT; return MONEY32_UNDEFINED; } return 0; }
static money32 BannerSetName(uint8 bannerIndex, uint16 nameChunkIndex, uint32 nameChunk1, uint32 nameChunk2, uint32 nameChunk3, uint8 flags) { static char newName[128]; if (bannerIndex >= MAX_BANNERS) { log_warning("Invalid game command for setting banner name, banner id = %d", bannerIndex); return MONEY32_UNDEFINED; } rct_banner* banner = &gBanners[bannerIndex]; gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_RUNNING_COSTS; size_t indexToOffset[3] = { 24, 0, 12 }; if (nameChunkIndex > Util::CountOf(indexToOffset)) { log_warning("Invalid chunk index for setting banner name, banner id = %d, index = %d", bannerIndex, nameChunkIndex); return MONEY32_UNDEFINED; } size_t nameChunkOffset = std::min<size_t>(indexToOffset[nameChunkIndex], Util::CountOf(newName) - 12); std::memcpy(&newName[0 + nameChunkOffset], &nameChunk1, sizeof(uint32)); std::memcpy(&newName[4 + nameChunkOffset], &nameChunk2, sizeof(uint32)); std::memcpy(&newName[8 + nameChunkOffset], &nameChunk3, sizeof(uint32)); if (nameChunkIndex != 0) { return 0; } if (!(flags & GAME_COMMAND_FLAG_APPLY)) { return 0; } utf8 *buffer = gCommonStringFormatBuffer; utf8 *dst = buffer; dst = utf8_write_codepoint(dst, FORMAT_COLOUR_CODE_START + banner->text_colour); String::Set(dst, 256, newName, 32); rct_string_id stringId = user_string_allocate(USER_STRING_DUPLICATION_PERMITTED, buffer); if (stringId != 0) { rct_string_id prevStringId = banner->string_idx; banner->string_idx = stringId; user_string_free(prevStringId); auto intent = Intent(INTENT_ACTION_UPDATE_BANNER); intent.putExtra(INTENT_EXTRA_BANNER_INDEX, bannerIndex); context_broadcast_intent(&intent); } else { gGameCommandErrorText = STR_ERR_CANT_SET_BANNER_TEXT; return MONEY32_UNDEFINED; } return 0; }
static void twitch_parse_followers() { struct AudienceMember { const char *name; bool isFollower; bool isInChat; bool isMod; bool exists; bool shouldTrack; }; std::vector<AudienceMember> members; http_json_response *jsonResponse = _twitchJsonResponse; if (json_is_array(jsonResponse->root)) { int audienceCount = json_array_size(jsonResponse->root); for (int i = 0; i < audienceCount; i++) { json_t *audienceMember = json_array_get(jsonResponse->root, i); if (!json_is_object(audienceMember)) continue; json_t *name = json_object_get(audienceMember, "name"); json_t *isFollower = json_object_get(audienceMember, "isFollower"); json_t *isInChat = json_object_get(audienceMember, "inChat"); json_t *isMod = json_object_get(audienceMember, "isMod"); AudienceMember member; member.name = json_string_value(name); member.isFollower = json_boolean_value(isFollower); member.isInChat = json_boolean_value(isInChat); member.isMod = json_boolean_value(isMod); member.exists = false; member.shouldTrack = false; if (member.name == NULL || member.name[0] == 0) continue; if (member.isInChat && gConfigTwitch.enable_chat_peep_tracking) member.shouldTrack = true; else if (member.isFollower && gConfigTwitch.enable_follower_peep_tracking) member.shouldTrack = true; if (gConfigTwitch.enable_chat_peep_names && member.isInChat) members.push_back(member); else if (gConfigTwitch.enable_follower_peep_names && member.isFollower) members.push_back(member); } uint16 spriteIndex; rct_peep *peep; char buffer[256]; // Check what followers are already in the park FOR_ALL_GUESTS(spriteIndex, peep) { if (is_user_string_id(peep->name_string_idx)) { format_string(buffer, peep->name_string_idx, NULL); AudienceMember *member = NULL; for (size_t i = 0; i < members.size(); i++) { if (_strcmpi(buffer, members[i].name) == 0) { member = &members[i]; members[i].exists = true; break; } } if (peep->peep_flags & PEEP_FLAGS_TWITCH) { if (member == NULL) { // Member no longer peep name worthy peep->peep_flags &= ~(PEEP_FLAGS_TRACKING | PEEP_FLAGS_TWITCH); // TODO set peep name back to number / real name } else { if (member->shouldTrack) peep->peep_flags |= (PEEP_FLAGS_TRACKING); else if (!member->shouldTrack) peep->peep_flags &= ~(PEEP_FLAGS_TRACKING); } } else if (member != NULL && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)) { // Peep with same name already exists but not twitch peep->peep_flags |= PEEP_FLAGS_TWITCH; if (member->shouldTrack) peep->peep_flags |= PEEP_FLAGS_TRACKING; } } } // Rename non-named peeps to followers that aren't currently in the park. if (members.size() > 0) { int memberIndex = -1; FOR_ALL_GUESTS(spriteIndex, peep) { int originalMemberIndex = memberIndex; for (size_t i = memberIndex + 1; i < members.size(); i++) { if (!members[i].exists) { memberIndex = i; break; } } if (originalMemberIndex == memberIndex) break; AudienceMember *member = &members[memberIndex]; if (!is_user_string_id(peep->name_string_idx) && !(peep->peep_flags & PEEP_FLAGS_LEAVING_PARK)) { // Rename peep and add flags rct_string_id newStringId = user_string_allocate(4, member->name); if (newStringId != 0) { peep->name_string_idx = newStringId; peep->peep_flags |= PEEP_FLAGS_TWITCH; if (member->shouldTrack) peep->peep_flags |= PEEP_FLAGS_TRACKING; } } else { // Peep still yet to be found for member memberIndex--; } }