STORE_CHARACTER_MALE_GENDER_NAME = "male"
STORE_CHARACTER_FEMALE_GENDER_NAME = "female"
STORE_CHARACTER_DEFAULT_MALE_FIGURE = "athletic"
STORE_CHARACTER_DEFAULT_FEMALE_FIGURE = "athletic"
STORE_CHARACTER_CHEEK_MAKEUP_INDEX = 1
STORE_CHARACTER__ENTIRE_FACE_MAKEUP_INDEX = 3
local INVALID_STATE = 0
local LINEUP_STATE = 1
local GALLERY_DOWNLOAD_STATE = 2
local CUSTOMIZATION_STATE = 3
local GALLERY_FULL_STATE = 4
local FINISHED_STATE = 5
Store_character_current_state = INVALID_STATE
Store_character_new_state_on_validation = INVALID_STATE
local SCALE_FONT_STORE_CHARACTER_LIST = 0.8
local Online_check_thread = -1
-- Menu Tiers
local MENU_TIER = 1
local BUILD_TIER = 2
local SKIN_COLOR_TIER = 3
local EYE_COLOR_TIER = 4
local HAIR_COLOR_TIER = 5
local MAKEUP_COLOR_TIER = 6
local LINEUP_TIER = 7
local Current_tier = MENU_TIER
Store_character_doc_handle = -1
-- Used to determine which of three colors player is currently picking, and the index in the color grid is currently chosen.
local Color_grid_idx = 1
local Color_slot_chosen = 0
Color_grid_data = {}
local Prev_index = -1
local Input_tracker_body_morph_select = nil
Hint_bar_mouse_input_tracker = -1
local Triangle_select_button_handle = -1
local Triangle_select_text_handle = -1
local Susan_mouse_area_img_h
Store_character_in_anim = -1
Store_character_out_anim = -1
Player_creation_bg_anim = -1
Iad_logo_anim = -1
Iad_bg_anim = -1
-- Hint bar and store header vdo's.
local Not_popup_grp = -1
local Reward_image = -1
local Triangle_select = -1
Lineup_input_tracker = {}
Lineup_mouse_input_tracker = {}
Store_character_current_preset_gender = -1
Store_character_current_preset_race = -1
Store_character_current_preset_figure = -1
--Store_character_rand_func
Store_character_randomizing_morph_set = -1
Store_character_building_morph_set = -1
-- Save current preset settings in case they are needed for a revert.
Store_character_preset_previous_settings = -1
Store_character_confirm_randomize = true
MAX_FACIAL_FEATURES = 10
-- variable to help control when player customization is actually ready
Store_lineup_is_loaded = true
-- Compositing data
--------------------------------------------
Store_character_composite_slots = {
["facial hair"] = {},
["eyebrows"] = {},
["body mask"] = {},
["makeup eyeliner"] = {},
["makeup eyeshadow"] = {},
["makeup cheeks"] = {},
["lips"] = {},
["makeup entire face"] = {},
["face items"] = {},
["face features"] = {},
["fingernails"] = {},
}
-- Composite actions; enum must match C code
COMPOSITE_REPLACE = 0
COMPOSITE_ADD = 1
COMPOSITE_REMOVE = 2
Store_character_current_composite_area = -1
Store_character_current_composite_item = -1
Store_character_current_composite_color = -1
Store_character_current_composite_alpha = -1
Store_character_allow_multiple_composites = -1
Store_character_composites_allowed = -1
Store_character_composites_used = -1
--------------------------------------------
CHARACTER_COLOR_POOL_TYPE = 0
HAIR_ITEM_SLOT = 1
Store_character_hair_acc_info = {
colors = { [0] = { }, [1] = { }, [2] = { } }
}
-- Save off current clothing color index if player cancels change
local Color_prev_color = 0
Store_character_hair_values = {}
-- Stores skin color selection when the player started, in case they preview new choices but revert.
Store_character_skin_color_current_setting = -1
Store_character_skin_color_selected = 0
Store_character_eye_selected = 0
Store_character_movement_anim_grp = -1
-- Save eye color for reverting
Store_character_orig_eye_color = -1
-- Save off morph values in case player reverts changes
Store_character_strength = -1
Store_character_skinny = -1
Store_character_fat = -1
Store_character_build_cursor_x = -1
Store_character_build_cursor_y = -1
Store_character_nav_thread = -1
local Store_character_nav_value = nil
Store_character_preset_update_thread = -1
local Store_character_preset_update_data = nil
Store_character_preset_new_gender = -1
Store_character_init_camera_moved = false
Store_character_anim_done = true
Store_character_original_anim_id = -1
-- Do we need to display a popup to confirm if the player should cancel?
Store_character_cancel_confirm_needed = false
-- Save off preset info while waiting for a confirmation of setting it.
Store_character_tentative_preset_data = -1
local Game_platform = -1
-- Global referencse to the "lazy susan" vdo for the character lineup. Needed for VDO callbacks.
Susan_object = -1
-- Number of images in the character lineup
STORE_CHARACTER_NUM_LINEUP_IMAGES = 9
STORE_CHARACTER_NUM_LINEUP_IMAGES_LOADED = 5
-- Table of character lineup preset settings
Store_character_lineup_presets = {
{ gender = "male", race = "white", figure = "athletic" },
{ gender = "female", race = "white", figure = "athletic" },
{ gender = "male", race = "black", figure = "athletic" },
{ gender = "female", race = "black", figure = "athletic" },
{ gender = "male", race = "hispanic", figure = "athletic" },
{ gender = "female", race = "hispanic", figure = "athletic" },
{ gender = "male", race = "asian", figure = "athletic" },
{ gender = "female", race = "asian", figure = "athletic" },
}
-- Flag to indicate if we've finished loading/animating in the character lineup.
Store_character_lineup_init_complete = false
local Store_character_clothing_load_thread = -1
local Store_character_clothes_on_current = true
local Store_character_clothes_on_pending = true
function store_character_fail_msg(event)
Store_common_popup:nav_enable(false, nil, nil)
game_UI_audio_play("UI_Main_Menu_Select")
Not_popup_grp:set_alpha(1)
Active_list:set_visible(true)
store_character_enable_mouse(true)
end
-- Player creation utility functions
--------------------------------------------
function store_character_change_clothes()
while true do
delay(0.1)
if pcu_is_streaming_done() then
-- Must wait at least a frame for handshake after streaming is done
delay(0.2)
if Store_character_clothes_on_current ~= Store_character_clothes_on_pending then
if Store_character_clothes_on_pending then
pcr_restore_clothing()
else
pcr_strip_clothing()
end
Store_character_clothes_on_current = Store_character_clothes_on_pending
end
end
end
end
-- This function is used to strip the clothes off the player when they enter submenus from the character creation screen.
--
function store_character_creation_strip_clothes()
if pcr_is_plastic_surgeon() == false then
if Store_character_clothing_load_thread == -1 then
Store_character_clothing_load_thread = thread_new("store_character_change_clothes")
end
Store_character_clothes_on_pending = false
end
end
-- This function is used to return the clothes on the player when they enter the top menu of the character creation screen.
--
function store_character_creation_add_clothes()
if pcr_is_plastic_surgeon() == false then
if Store_character_clothing_load_thread == -1 then
Store_character_clothing_load_thread = thread_new("store_character_change_clothes")
end
Store_character_clothes_on_pending = true
end
end
-- Compositing functions --- Possibly move to store_common so tatoos can use them????
--------------------------------------------
-- Save current info on one composite slot
--
function store_character_composite_save_info(slot_name)
slot_name = slot_name or Store_character_current_composite_area
local index, color, alpha = pcr_get_composite_info(slot_name)
Store_character_composite_slots[slot_name].index = index
Store_character_composite_slots[slot_name].color = color
Store_character_composite_slots[slot_name].alpha = alpha
end
-- Restore to saved info on one composite slot
--
function store_character_composite_restore_info(slot_name)
-- kill update thread if it's still around
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
slot_name = slot_name or Store_character_current_composite_area
local values = Store_character_composite_slots[slot_name]
pcr_change_composite(slot_name, values.index, values.color, values.alpha)
end
-- Build a menu based on compositing choices
--
function store_character_composite_menu_build(menu_data)
Store_character_current_composite_area = menu_data.composite_area
-- save off current settings
store_character_composite_save_info()
-- Get the colors for the palette.
-- We need to do this now, because we need to find and save off the current composite the player has in case they revert.
Color_grid_data = {}
Color_grid_idx = 1
pcr_report_composite_colors(Store_character_current_composite_area, true, "store_character_composite_add_color")
-- Initialize the current settings
local values = Store_character_composite_slots[Store_character_current_composite_area]
Store_character_current_composite_item = values.index
Store_character_current_composite_color = values.color
Store_character_current_composite_alpha = values.alpha
if menu_data.max_selections ~= nil and menu_data.max_selections > 1 then
Store_character_allow_multiple_composites = true
Store_character_composites_used = 0
Store_character_composites_allowed = menu_data.max_selections
else
Store_character_allow_multiple_composites = false
end
-- Special case: body mask or menus with multiple selections don't have a "none" choice
if menu_data.composite_area ~= "body mask" and Store_character_allow_multiple_composites == false then
local item = {
label = "CUST_ITEM_NOTHING", type = TYPE_BUTTON, composite_item = -1, on_nav = store_character_composite_item_nav,
on_select = store_character_composite_item_set, on_cancel = store_character_composite_item_revert
}
local menu_idx = #Store_common_sub_menu + 1
Store_common_sub_menu[menu_idx] = item
end
pcr_report_composite_items(Store_character_current_composite_area, "store_character_composite_item_add")
end
function store_character_composite_color_grid_show(menu_data)
Active_list:set_visible(false)
Current_tier = MAKEUP_COLOR_TIER
store_character_enable_mouse(false, false)
-- display the grid
--input_data, current_option, max_width, max_height, button_w, button_h, hint_button_offset, current_category, outline_scale, background_enabled, show_current_button, grid_padding, set_size
Store_common_color_grid:draw_items(Color_grid_data, Color_grid_idx, 9, 7, 53.5, 53.5, 15, nil, 1.1, true, true, -7, true)
Store_common_color_grid:set_visible(true)
Store_common_color_grid:nav_enable(true, "store_character_composite_color_select", "store_character_composite_color_grid_nav")
Color_grid_data.alpha_select = menu_data.alpha_select
end
function store_character_composite_color_grid_nav(event, value)
store_character_grid_nav(event, value)
-- set the color and preview the item
local idx = Store_common_color_grid:return_selected_index()
Store_character_current_composite_color = Color_grid_data[idx].index
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
Store_character_nav_thread = thread_new("store_character_composite_item_delayed_set")
end
function store_character_composite_add_color(color_index, label_crc, label_str, red, green, blue, max_colors, is_current)
local grid_idx = #Color_grid_data + 1
Color_grid_data[grid_idx] = {
icon = "ui_menu_veh_pnt_base",
label = label_str,
label_crc = label_crc,
color = {red = red, green = green, blue = blue},
index = color_index,
}
if is_current == true then
Color_grid_idx = grid_idx
Store_character_current_composite_color = Color_grid_data[grid_idx].index
end
end
function store_character_composite_alpha_done()
game_UI_audio_play("UI_Main_Menu_Select")
store_character_composite_save_info()
store_common_back_menu()
store_common_back_menu()
end
function store_character_composite_alpha_revert()
game_UI_audio_play("UI_Main_Menu_Nav_Back")
end
function store_character_composite_alpha_change(menu_data)
Store_character_current_composite_alpha = menu_data.current_value
store_character_composite_update()
end
function store_character_composite_show_alpha_menu()
Active_list:set_visible(true)
-- Get data to populate the megalist
local New_menu_data = {
{ label = "CUST_OPACITY_SLIDER", type = TYPE_SLIDER, on_select = store_character_composite_alpha_done, on_cancel = store_character_composite_alpha_revert,
on_value_update = store_character_composite_alpha_change, min = 0, max = 1, step = 0.05, current_value = Store_character_current_composite_alpha },
}
store_common_push_menu(New_menu_data)
end
function store_character_composite_color_select(event)
Store_common_color_grid:nav_enable(false, nil, nil)
Store_common_color_grid:cleanup()
Store_common_color_grid:set_visible(false)
Active_list:set_visible(true)
Current_tier = MENU_TIER
store_character_enable_mouse(true, false)
-- Did the player select a color?
if event == "select" or event == "mouse_click" then
game_UI_audio_play("UI_Main_Menu_Select")
if Color_grid_data.alpha_select then
store_character_composite_show_alpha_menu()
else
store_character_composite_save_info()
store_common_back_menu()
Color_grid_data = {}
end
else
game_UI_audio_play("UI_Main_Menu_Nav_Back")
end
end
function store_character_composite_item_set(menu_data)
-- Toggle selection if multiple selections allowed
if Store_character_allow_multiple_composites then
Store_character_current_composite_item = menu_data.composite_item
if menu_data.equipped then
store_character_composite_update(COMPOSITE_REMOVE)
else
-- Have we hit the maximum number?
if Store_character_composites_used >= Store_character_composites_allowed then
Store_common_popup:populate_list(Ok_choice, 1, STORE_COMMON_LIST_SIZE, 1)
Store_common_popup:set_title("MENU_TITLE_NOTICE")
Store_common_popup:set_text("CUST_MAX_COMPOSITE_WARNING")
Store_common_popup:nav_enable(true, "store_character_fail_msg", "store_character_popup_nav")
Not_popup_grp:set_alpha(.5)
Active_list:set_visible(false)
store_character_enable_mouse(false)
return
else
store_character_composite_update(COMPOSITE_ADD)
end
end
-- refresh the menu
local index = Active_list:get_selection()
Store_character_composites_used = 0
Store_common_sub_menu = {}
pcr_report_composite_items(Store_character_current_composite_area, "store_character_composite_item_add")
Menu_data = Store_common_sub_menu
Store_common_sub_menu = {}
--Rather than reupdate the list we will only reupdate a single item (JMH 9/20/2011)
--store_character_populate_list(Menu_data, index)
Active_list.data = Menu_data
Active_list:refresh_single_item(index)
if Mouse_input_tracker ~= 0 then
Mouse_input_tracker:remove_all()
Active_list:add_mouse_inputs("store_common", Mouse_input_tracker)
Mouse_input_tracker:subscribe(true)
end
return
end
Store_character_current_composite_item = menu_data.composite_item
if menu_data.composite_item ~= -1 then
-- only some things do we choose color for
if menu_data.colorized then
store_character_composite_color_grid_show(menu_data)
else
if menu_data.alpha_select then
store_character_composite_show_alpha_menu()
else
store_character_composite_save_info()
store_common_back_menu()
end
end
else
store_common_back_menu()
end
end
-- Player reverted and didn't make a composite change. Revert previewed composite back to what it was.
--
function store_character_composite_item_revert()
-- Don't revert for multiple selections allowed
if Store_character_allow_multiple_composites then
return
end
store_character_composite_restore_info()
end
function store_character_composite_update(optional_action)
optional_action = optional_action or COMPOSITE_REPLACE
pcr_change_composite(Store_character_current_composite_area, Store_character_current_composite_item, Store_character_current_composite_color, Store_character_current_composite_alpha, optional_action)
Store_character_confirm_randomize = true
end
function store_character_composite_item_delayed_set()
delay(0.4)
store_character_composite_update()
Store_character_nav_thread = -1
end
function store_character_composite_item_nav(menu_data)
-- Don't preview for multiple selections allowed
if Store_character_allow_multiple_composites then
return
end
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
Store_character_current_composite_item = menu_data.composite_item
-- reset alpha to 1
Store_character_current_composite_alpha = 1
Store_character_nav_thread = thread_new("store_character_composite_item_delayed_set")
end
function store_character_composite_item_add(composite_index, name_crc, name_str, current, colorized, alpha_select, unused_price, unused_respect)
local item = {
type = TYPE_BUTTON, composite_item = composite_index, label = name_str, label_crc = name_crc, on_nav = store_character_composite_item_nav,
on_select = store_character_composite_item_set, on_cancel = store_character_composite_item_revert, colorized = colorized, alpha_select = alpha_select,
}
if Store_character_allow_multiple_composites then
item.equipped = current
if current then
Store_character_composites_used = Store_character_composites_used + 1
end
end
local menu_idx = #Store_common_sub_menu + 1
if current == true then
Store_common_sub_menu.start_index = menu_idx
end
Store_common_sub_menu[menu_idx] = item
end
-- Preset menus
--------------------------------------------
function store_character_preset_enter_main(menu_data)
-- Get initial preset values
Store_character_current_preset_gender, Store_character_current_preset_race, Store_character_current_preset_figure = pcr_get_player_preset()
if Store_character_preset_update_thread == -1 then
Store_character_preset_update_thread = thread_new("store_character_preset_update_thread")
end
end
function store_character_preset_cleanup(menu_data)
Store_character_preset_update_data = nil
if Store_character_preset_update_thread ~= -1 then
thread_kill(Store_character_preset_update_thread)
Store_character_preset_update_thread = -1
end
end
function store_character_preset_update_thread()
while true do
delay(0.5)
local update_data = Store_character_preset_update_data
Store_character_preset_update_data = nil
if update_data ~= nil then
store_character_apply_preset(update_data)
end
if Store_character_preset_update_thread == -1 then
break
end
end
end
-- Player chose a preset - update the character
--
function store_character_preset_nav(menu_data)
Store_character_preset_update_data = menu_data
end
function store_character_apply_preset(menu_data)
local update_preset = false
local update_regional_preset = false
-- Age preset updating is handled differently. So check if this is an age update, and
-- if so, just do that and return.
--
if menu_data.preset_age ~= nil then
pcr_apply_morph_value("global body", "old", menu_data.preset_age)
pcr_mark_changed()
return
end
if menu_data.preset_gender ~= nil and menu_data.preset_gender ~= Store_character_current_preset_gender then
Store_character_preset_new_gender = menu_data.preset_gender
Store_character_current_preset_gender = menu_data.preset_gender
update_preset = true
if Store_character_current_preset_gender == STORE_CHARACTER_FEMALE_GENDER_NAME then
Store_character_current_preset_figure = STORE_CHARACTER_DEFAULT_FEMALE_FIGURE
else
Store_character_current_preset_figure = STORE_CHARACTER_DEFAULT_MALE_FIGURE
end
end
if menu_data.preset_race ~= nil and menu_data.preset_race ~= Store_character_current_preset_race then
Store_character_current_preset_race = menu_data.preset_race
update_preset = true
end
if menu_data.preset_figure ~= nil and menu_data.preset_figure ~= Store_character_current_preset_figure then
Store_character_current_preset_figure = menu_data.preset_figure
update_regional_preset = true;
end
if update_preset == true then
local preset_name = Store_character_current_preset_gender .. "_" .. Store_character_current_preset_race
local regional_preset_name = Store_character_current_preset_gender .. "_" .. Store_character_current_preset_figure;
pcr_apply_preset(preset_name, "figure", regional_preset_name, true)
pcr_set_triangle_cursor_pos(-1, -1)
pcr_mark_changed()
Store_character_confirm_randomize = true
else
if update_regional_preset == true then
pcr_apply_regional_preset("figure", Store_character_current_preset_gender .. "_" .. Store_character_current_preset_figure)
pcr_set_triangle_cursor_pos(-1, -1)
pcr_mark_changed()
Store_character_confirm_randomize = true
end
end
end
-- Player selected a preset with the "select" button. We need to confirm if they really want to do this first.
--
function store_character_select_preset(menu_data)
store_character_enable_mouse(false)
Store_common_popup:populate_list(Yes_no_choices, 2, STORE_COMMON_LIST_SIZE, 2)
Store_common_popup:set_title("MENU_TITLE_WARNING")
Store_common_popup:set_text("PRESET_WARNING_BODY")
Store_common_popup:nav_enable(true, "store_character_select_preset_final", "store_character_popup_nav")
Not_popup_grp:set_alpha(.5)
Active_list:set_visible(false)
Store_character_tentative_preset_data = menu_data
return true
end
function store_character_select_preset_final(event)
Store_common_popup:nav_enable(false, nil, nil)
Not_popup_grp:set_alpha(1)
Active_list:set_visible(true)
-- The user hit the B button and cancelled the change
-- This is redundant to selecting "No" in the list
if event == "back" then
game_UI_audio_play("UI_Main_Menu_Nav_Back")
store_character_enable_mouse(true)
return
end
game_UI_audio_play("UI_Main_Menu_Select")
-- Did we select yes? This assumes yes is the first item
if Store_common_popup:get_selected_data() ~= 1 then
store_character_enable_mouse(true)
return
end
-- Change to the preset
store_character_apply_preset(Store_character_tentative_preset_data)
store_common_back_menu()
end
-- Player cancelled a preset with the "back" button.
--
function store_character_revert_preset(menu_data)
-- Pass in an previous saved setting to do a revert.
Store_character_preset_update_data = nil
-- do a revert if we had previous settings
if Store_character_preset_previous_settings ~= nil then
store_character_apply_preset(Store_character_preset_previous_settings)
end
end
-- Save the current figure setting in case we need to revert.
--
function store_character_preset_save_figure(menu_data)
Store_character_current_preset_gender, Store_character_current_preset_race, Store_character_current_preset_figure = pcr_get_player_preset()
local data = { preset_figure = Store_character_current_preset_figure }
Store_character_preset_previous_settings = data
end
-- Save the current race setting in case we need to revert.
--
function store_character_preset_save_race(menu_data)
Store_character_current_preset_gender, Store_character_current_preset_race, Store_character_current_preset_figure = pcr_get_player_preset()
local data = { preset_race = Store_character_current_preset_race }
Store_character_preset_previous_settings = data
end
-- Save the current age setting in case we need to revert.
--
function store_character_preset_save_age(menu_data)
local data = { preset_age = pcr_get_morph_value("Global Body", "old") }
Store_character_preset_previous_settings = data
end
function store_character_preset_sex_fill(menu_data)
local store_character_presets_sex = {
{ label = "PLAYER_CREATION_PRESET_MALE", name = "male", type = TYPE_BUTTON, on_select = store_character_select_preset, on_cancel = store_character_revert_preset, preset_gender = "male" },
{ label = "PLAYER_CREATION_PRESET_FEMALE", name = "female", type = TYPE_BUTTON, on_select = store_character_select_preset, on_cancel = store_character_revert_preset, preset_gender = "female" },
}
store_character_preset_save_race(menu_data)
Store_common_sub_menu = store_character_presets_sex
for i = 1, #Store_common_sub_menu do
if Store_common_sub_menu[i].name == Store_character_current_preset_gender then
Store_common_sub_menu.start_index = i
end
end
end
function store_character_preset_race_fill(menu_data)
local store_character_presets_race = {
{ label = "RACE_AFRICAN", name = "black", type = TYPE_BUTTON, on_select = store_character_select_preset, on_cancel = store_character_revert_preset, on_nav = store_character_preset_nav, preset_race = "black" },
{ label = "RACE_ASIAN", name = "asian", type = TYPE_BUTTON, on_select = store_character_select_preset, on_cancel = store_character_revert_preset, on_nav = store_character_preset_nav, preset_race = "asian" },
{ label = "RACE_CAUCASIAN", name = "white", type = TYPE_BUTTON, on_select = store_character_select_preset, on_cancel = store_character_revert_preset, on_nav = store_character_preset_nav, preset_race = "white" },
{ label = "RACE_HISPANIC", name = "hispanic", type = TYPE_BUTTON, on_select = store_character_select_preset, on_cancel = store_character_revert_preset, on_nav = store_character_preset_nav, preset_race = "hispanic" },
}
store_character_preset_save_race(menu_data)
Store_common_sub_menu = store_character_presets_race
for i = 1, #Store_common_sub_menu do
if Store_common_sub_menu[i].name == Store_character_current_preset_race then
Store_common_sub_menu.start_index = i
end
end
end
-- Body menus
--------------------------------------------
-- Input constants
RAW_INPUT_DEAD_ZONE = 0.1
LEFT_STICK_PIXELS_PER_FRAME = 4
Left_stick_current_input_x = 0
Left_stick_current_input_y = 0
Left_stick_input_thread = -1
Mouse_x = -1
Mouse_y = -1
-- Helper function to adjust the raw input based on dead zone and pixel speed
local function input_from_stick_value_and_speed(value, speed)
if value > RAW_INPUT_DEAD_ZONE then
return (value - RAW_INPUT_DEAD_ZONE) / (1 - RAW_INPUT_DEAD_ZONE) * LEFT_STICK_PIXELS_PER_FRAME
elseif value < -RAW_INPUT_DEAD_ZONE then
return (value + RAW_INPUT_DEAD_ZONE) / (1 - RAW_INPUT_DEAD_ZONE) * LEFT_STICK_PIXELS_PER_FRAME
else
return 0
end
end
function store_character_move_left_x(event, value)
Left_stick_current_input_x = input_from_stick_value_and_speed(value, LEFT_STICK_PIXELS_PER_FRAME)
end
function store_character_move_left_y(event, value)
Left_stick_current_input_y = input_from_stick_value_and_speed(value, LEFT_STICK_PIXELS_PER_FRAME)
end
function store_character_move_left_stick_thread()
local loop_count = 0
local update_needed = false
local new_str, new_skin, new_fat, new_x, new_y
while true do
if Left_stick_current_input_x ~= 0 or Left_stick_current_input_y ~= 0 then
if Store_common_popup:get_property("visible") ~= true then
local str, skin, fat, x, y = Triangle_select:move_left_stick(Left_stick_current_input_x, Left_stick_current_input_y)
if str ~= nil then
new_str = str
new_skin = skin
new_fat = fat
new_x = x
new_y = y
update_needed = true
end
end
elseif Mouse_x ~= -1 and Mouse_y ~= -1 then
local str, skin, fat, x, y = Triangle_select:update_mouse(Mouse_x, Mouse_y)
if str ~= nil then
new_str = str
new_skin = skin
new_fat = fat
new_x = x
new_y = y
update_needed = true
end
-- Reset mouse values for the mouse event handlers to set in next frame
Mouse_x = -1
Mouse_y = -1
end
debug_print("vint", "Thread Running... \n")
delay(1/20) -- Peg to 20 frames/sec
-- only update the character's body at most every X times through this loop
if loop_count > 0 then
loop_count = 0
if update_needed then
-- actually morph the character
--game_UI_audio_play("UI_Customization_Triangle")
pcr_apply_morph_value("Global Body", "body muscle", new_str)
pcr_apply_morph_value("Global Body", "body skinny", new_skin)
pcr_apply_morph_value("Global Body", "body fat", new_fat)
pcr_set_triangle_cursor_pos(new_x, new_y)
if floor(new_x) ~= floor(Store_character_build_cursor_x) or floor(new_y) ~= floor(Store_character_build_cursor_y) then
Store_character_cancel_confirm_needed = true
end
update_needed = false
end
end
loop_count = loop_count + 1
if Left_stick_input_thread == -1 then
debug_print("vint", "Die... \n")
break
end
end
end
-- empty function to take priority over normal input for the store while we're in the triangle selector
function store_character_null(event, value)
end
function store_character_load_triangle()
store_unlock_controls()
Store_character_strength = pcr_get_morph_value("Global Body", "body muscle")
Store_character_skinny = pcr_get_morph_value("Global Body", "body skinny")
Store_character_fat = pcr_get_morph_value("Global Body", "body fat")
Store_character_build_cursor_x, Store_character_build_cursor_y = pcr_get_triangle_cursor_pos()
Triangle_select:init_morphs(Store_character_strength, Store_character_skinny, Store_character_fat, Store_character_build_cursor_x, Store_character_build_cursor_y)
Triangle_select:set_visible(true)
local title = nil
local title_crc = nil
title_crc = Menu_data[Active_list:get_selection()].label_crc
if title_crc == nil then
title = Menu_data[Active_list:get_selection()].label
end
Store_header:push_title( title_crc, title )
end
function store_character_morph_show(menu_data)
Current_tier = BUILD_TIER
Active_list:set_visible(false)
store_character_enable_mouse(false, false)
if Input_tracker_body_morph_select == nil then
Input_tracker_body_morph_select = Vdo_input_tracker:new()
Input_tracker_body_morph_select:add_input("left_joy_x", "store_character_move_left_x", 100, true)
Input_tracker_body_morph_select:add_input("left_joy_y", "store_character_move_left_y", 100, true)
Input_tracker_body_morph_select:add_input("select", "store_character_morph_select", 100)
Input_tracker_body_morph_select:add_input("back", "store_character_morph_revert_confirm", 100)
Input_tracker_body_morph_select:add_input("nav_up", "store_character_null", 100)
Input_tracker_body_morph_select:add_input("nav_down", "store_character_null", 100)
Input_tracker_body_morph_select:add_input("nav_left", "store_character_null", 100)
Input_tracker_body_morph_select:add_input("nav_right", "store_character_null", 100)
if Game_platform == "PC" then
store_character_enable_mouse(false, false)
-- Add mouse input for the "confirm selection" button
local confirm_handle = vint_object_find("btn_highlight", Triangle_select.handle)
Triangle_select_button_handle = vint_object_find("bg", confirm_handle)
Input_tracker_body_morph_select:add_mouse_input("mouse_click", "store_character_mouse_click", 100, Triangle_select_button_handle)
Input_tracker_body_morph_select:add_mouse_input("mouse_move", "store_character_mouse_move", 100, Triangle_select_button_handle)
local text_handle = vint_object_find("btn_select", Triangle_select.handle)
Triangle_select_text_handle = vint_object_find("toggle_text", text_handle)
-- Add mouse input for the build triangle
local triangle_handle = vint_object_find("triangle_base", Triangle_select.handle)
Input_tracker_body_morph_select:add_mouse_input("mouse_click", "store_character_mouse_click", 100, triangle_handle)
Input_tracker_body_morph_select:add_mouse_input("mouse_move", "store_character_mouse_move", 100, triangle_handle)
Input_tracker_body_morph_select:add_mouse_input("mouse_drag", "store_character_mouse_drag", 100, triangle_handle)
Input_tracker_body_morph_select:add_mouse_input("mouse_drag_release", "store_character_mouse_drag_release", 100, triangle_handle)
end
end
Input_tracker_body_morph_select:subscribe(true)
--Load triangle bitmap
game_peg_load_with_cb("store_character_load_triangle", 1, "ui_bms_pcr_tri")
store_lock_controls()
Left_stick_input_thread = thread_new("store_character_move_left_stick_thread")
end
function store_character_morph_select()
-- Nuke all button subscriptions
Input_tracker_body_morph_select:subscribe(false)
game_UI_audio_play("UI_Main_Menu_Select")
if Left_stick_input_thread ~= -1 then
thread_kill(Left_stick_input_thread)
Left_stick_input_thread = -1
end
-- Unload triangle bitmap
game_peg_unload("ui_bms_pcr_tri")
Triangle_select:set_visible(false)
Active_list:set_visible(true)
Current_tier = MENU_TIER
store_character_enable_mouse(true, false)
Store_header:pop_title()
end
-- If the player made changes to the triangle slider, ask if they want to throw them out.
--
function store_character_morph_revert_confirm()
if Store_character_cancel_confirm_needed == false then
store_character_morph_revert()
return
end
store_character_enable_mouse(false)
if Input_tracker_body_morph_select ~= nil then
Input_tracker_body_morph_select:subscribe(false)
end
Store_common_popup:populate_list(Yes_no_choices, 2, STORE_COMMON_LIST_SIZE, 2)
Store_common_popup:set_title("MENU_TITLE_WARNING")
Store_common_popup:set_text("CANCEL_WARNING_BODY")
Store_common_popup:nav_enable(true, "store_character_morph_revert_confirm_final", "store_character_popup_nav")
Not_popup_grp:set_alpha(.5)
end
-- Handle response to popup
--
function store_character_morph_revert_confirm_final(event)
Store_common_popup:nav_enable(false, nil, nil)
Not_popup_grp:set_alpha(1)
-- The user hit the B button and are cancelling the cancel :-)
-- This is redundant to selecting "No" in the list
if event == "back" then
game_UI_audio_play("UI_Main_Menu_Nav_Back")
--store_character_enable_mouse(true)
Input_tracker_body_morph_select:subscribe(true)
if Hint_bar_mouse_input_tracker ~= -1 then
Hint_bar_mouse_input_tracker:subscribe(true)
end
return
end
game_UI_audio_play("UI_Main_Menu_Select")
-- Did we select yes? This assumes yes is the first item
if Store_common_popup:get_selected_data() ~= 1 then
--store_character_enable_mouse(true)
Input_tracker_body_morph_select:subscribe(true)
if Hint_bar_mouse_input_tracker ~= -1 then
Hint_bar_mouse_input_tracker:subscribe(true)
end
return
end
-- Do the cancel
Store_character_cancel_confirm_needed = false
store_character_morph_revert()
end
-- Player reverted changes made in the triangle selector
function store_character_morph_revert()
-- Nuke all button subscriptions
Input_tracker_body_morph_select:subscribe(false)
if Left_stick_input_thread ~= -1 then
thread_kill(Left_stick_input_thread)
Left_stick_input_thread = -1
end
-- restore previous morph values
pcr_apply_morph_value("Global Body", "body muscle", Store_character_strength)
pcr_apply_morph_value("Global Body", "body skinny", Store_character_skinny)
pcr_apply_morph_value("Global Body", "body fat", Store_character_fat)
pcr_set_triangle_cursor_pos(Store_character_build_cursor_x, Store_character_build_cursor_y)
-- Unload triangle bitmap
game_peg_unload("ui_bms_pcr_tri")
Triangle_select:set_visible(false)
Active_list:set_visible(true)
Current_tier = MENU_TIER
store_character_enable_mouse(true)
Store_header:pop_title()
end
-- morph support
--
function store_character_morph_change(menu_data)
pcr_apply_morph_value(menu_data.morph_set, menu_data.morph_id, menu_data.current_value)
pcr_mark_changed()
Store_character_confirm_randomize = true
Store_character_cancel_confirm_needed = true
end
function store_character_skin_color_add(id, swatch_crc, label_crc, label_str, current)
local menu_idx = #Color_grid_data + 1
local item = {
index = menu_idx, id = id, icon_crc = swatch_crc, label_crc = label_crc, label = label_str, color = {red = 1, green = 1, blue = 1},
}
Color_grid_data[menu_idx] = item
if current == true then
Color_grid_idx = menu_idx
Store_character_skin_color_current_setting = menu_idx
end
end
function store_character_skin_grid_show(menu_data)
--Store_character_original_values.skin_color = nil
Color_grid_data = {}
Color_grid_idx = 1
pcr_report_skin_colors("store_character_skin_color_add")
-- Load Hair Swatches
game_peg_load_with_cb("store_character_skin_swatch_loaded",1,"ui_bms_pcr_skin")
store_lock_controls()
store_set_camera_pos("body")
end
function store_character_skin_color_delayed_set()
delay(0.4)
pcr_change_skin_color(Store_character_skin_color_selected)
Store_character_nav_value = nil
Store_character_nav_thread = -1
end
function store_character_skin_color_grid_nav(event, value)
store_character_grid_nav(event, value)
-- set the color and preview the item
local idx = Store_common_color_grid:return_selected_index()
Store_character_skin_color_selected = Color_grid_data[idx].id
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
Store_character_nav_thread = thread_new("store_character_skin_color_delayed_set")
end
function store_character_skin_swatch_loaded()
store_unlock_controls()
Store_common_color_grid:draw_items(Color_grid_data, Color_grid_idx, 5, 4, 83, 83, 26, nil, 1.1)
Store_common_color_grid:nav_enable(true, "store_character_skin_color_selected", "store_character_skin_color_grid_nav")
Store_common_color_grid:set_visible(true)
Active_list:set_visible(false)
Current_tier = SKIN_COLOR_TIER
store_character_enable_mouse(false, false)
-- set the titles on top
Store_header:push_title( nil, "CUST_SKIN")
end
function store_character_skin_color_selected(event)
-- remove skin color title
Store_header:pop_title()
-- Did the player select a color?
if event == "select" or event == "mouse_click" then
game_UI_audio_play("UI_Main_Menu_Select")
local idx = Store_common_color_grid:return_selected_index()
if idx <= #Color_grid_data then
pcr_change_skin_color(Color_grid_data[idx].id)
pcr_mark_changed()
end
else
game_UI_audio_play("UI_Main_Menu_Nav_Back")
if Store_character_nav_thread > -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
-- player didn't accept; revert change
if Store_character_skin_color_current_setting ~= -1 then
pcr_change_skin_color(Color_grid_data[Store_character_skin_color_current_setting].id)
end
end
game_peg_unload("ui_bms_pcr_skin")
Store_common_color_grid:nav_enable(false, nil, nil)
Store_common_color_grid:cleanup()
Store_common_color_grid:set_visible(false)
Active_list:set_visible(true)
Color_grid_data = {}
Current_tier = MENU_TIER
store_character_enable_mouse(true, false)
end
function store_character_body_menu_return(menu_data)
store_character_creation_strip_clothes()
-- check the current values for age and breast, since a preset will have reset them
local current_age = pcr_get_morph_value("global body", "old")
local current_breast = pcr_get_morph_value("global body", "breast")
menu_data[6].current_value = current_age
menu_data[7].current_value = current_breast
Store_character_current_preset_gender = pcr_get_player_preset()
local sex_camera
if Store_character_current_preset_gender == STORE_CHARACTER_FEMALE_GENDER_NAME then
sex_camera = "sex female"
else
sex_camera = "sex male"
end
menu_data[7].camera = sex_camera
-- Redraw the menu to show updated slider values and camera views
local start_index = store_common_stack_get_index()
store_character_populate_list(menu_data, start_index)
end
function store_character_camera_on_nav(menu_data)
if menu_data.camera ~= nil then
store_set_camera_pos(menu_data.camera)
end
end
function store_character_fill_global_menu(menu_data)
local current_age = pcr_get_morph_value("global body", "old")
local current_breast = pcr_get_morph_value("global body", "breast")
Store_character_current_preset_gender = pcr_get_player_preset()
local sex_camera
if Store_character_current_preset_gender == STORE_CHARACTER_FEMALE_GENDER_NAME then
sex_camera = "sex female"
else
sex_camera = "sex male"
end
local store_character_global_menu = {
on_enter = store_character_body_menu_return,
{ label = "CUST_PRESET_SEX", type = TYPE_BUTTON, on_sub_menu_fill = store_character_preset_sex_fill, camera = "body", on_nav = store_character_camera_on_nav },
{ label = "CUST_PRESET_RACE", type = TYPE_BUTTON, on_sub_menu_fill = store_character_preset_race_fill, camera = "body", on_nav = store_character_camera_on_nav },
{ label = "STORE_BUILD", type = TYPE_BUTTON, on_select = store_character_morph_show, camera = "body", on_nav = store_character_camera_on_nav },
{ label = "CUST_SKIN", type = TYPE_BUTTON, on_select = store_character_skin_grid_show, camera = "body", on_nav = store_character_camera_on_nav },
{ label = "CUST_BODY_MASK", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, camera = "body", on_nav = store_character_camera_on_nav, composite_area = "body mask", },
{ label = "CUST_PRESET_AGE", type = TYPE_SLIDER, on_value_update = store_character_morph_change, camera = "head", on_nav = store_character_camera_on_nav,
morph_id = "old", morph_set = "Global Body", min = 0, max = 1, step = 0.01, current_value = current_age, },
{ label = "CUST_SEX_APPEAL", type = TYPE_SLIDER, on_value_update = store_character_morph_change, camera = sex_camera, on_nav = store_character_camera_on_nav,
morph_id = "breast", morph_set = "Global Body", min = 0, max = 1, step = 0.01, current_value = current_breast, },
}
Store_common_sub_menu = store_character_global_menu
end
-- Hair menus
--------------------------------------------
-- Create the menu of hair style choices.
--
function store_character_hair_style_show(menu_data)
if Store_character_nav_thread > -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
-- save off current hair values
store_character_hair_item_save()
local item = {
type = TYPE_BUTTON,
hair_item = -1,
primary = Store_character_hair_values.primary,
label = "HAIR_CAT_NONE",
on_nav = store_character_hair_style_nav,
on_select = store_character_hair_item_select,
on_cancel = store_character_hair_item_revert,
}
local menu_idx = #Store_common_sub_menu + 1
Store_common_sub_menu[menu_idx] = item
pcr_report_hair_items(0, "store_character_hair_item_add")
end
-- Populate hair menus.
--
function store_character_hair_item_add(hair_item, label_crc, label_str, is_current)
local item = {
type = TYPE_BUTTON,
hair_item = hair_item,
primary = Store_character_hair_values.primary,
label = label_str,
label_crc = label_crc,
on_nav = store_character_hair_style_nav,
on_select = store_character_hair_item_select,
on_cancel = store_character_hair_item_revert
}
local menu_idx = #Store_common_sub_menu + 1
Store_common_sub_menu[menu_idx] = item
if is_current == true then
Store_common_sub_menu.start_index = menu_idx
end
end
-- Update hair preview while player navigates
--
function store_character_hair_style_nav(menu_data)
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
Store_character_nav_value = menu_data
Store_character_nav_thread = thread_new("store_character_delayed_set_hair_item")
end
-- Delayed previewing from thread of new hair style while navigating
--
function store_character_delayed_set_hair_item()
delay(0.4)
pcr_change_hair(0, Store_character_nav_value.hair_item, Store_character_nav_value.primary, 0)
Store_character_nav_thread = -1
end
-- Player has chosen a hair style and hit accept. Update, and choose hair colors
--
function store_character_hair_item_select(menu_data)
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
pcr_change_hair(0, menu_data.hair_item, menu_data.primary, 0)
pcr_mark_changed()
Store_character_confirm_randomize = true
-- Go on to color selection
if menu_data.hair_item ~= -1 then
store_character_hair_color_grid_show(menu_data)
else
-- Don't choose color if hair choice was "none"
store_character_hair_item_save()
store_common_back_menu()
end
end
-- Player backed out of hair selection without hitting accept. Revert to previous hair.
--
function store_character_hair_item_revert(menu_data)
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
-- Do the revert
pcr_change_hair(0, Store_character_hair_values.style, Store_character_hair_values.primary, 0)
end
function store_character_hair_item_save(menu_data)
Store_character_hair_values.style, Store_character_hair_values.primary = pcr_get_hair_info(0)
end
-- Build the hair color selection grid list. Start asynchronous load of hair color swatch.
--
function store_character_hair_color_grid_show(menu_data)
Color_grid_data = {}
-- menu_data.hair_item used if we've changed the hair style and directly went to color selection... otherwise used saved value if just picking color
Color_grid_data.hair_item = menu_data.hair_item or Store_character_hair_values.style
Color_grid_idx = 1
pcr_report_hair_colors("store_character_hair_color_add")
-- Load Hair Swatches
game_peg_load_with_cb("store_character_hair_swatch_loaded",1,"ui_bms_pcr_hair")
store_lock_controls()
end
function store_character_hair_color_add(id, swatch_crc, label_crc, label_str, current, swatch_label)
local menu_idx = #Color_grid_data + 1
local item = {
index = menu_idx,
id = id,
label_crc = label_crc,
label = label_str,
icon_crc = swatch_crc,
color = {red = 1, green = 1, blue = 1},
hair_item = Color_grid_data.hair_item,
primary = id,
}
-- set start index if this is the current hair color
if current == true then
Color_grid_idx = menu_idx
end
Color_grid_data[menu_idx] = item
end
function store_character_hair_swatch_loaded()
store_unlock_controls()
Store_common_color_grid:draw_items(Color_grid_data, Color_grid_idx, 5, 4, 83, 83, 26, nil, 1.1)
Store_common_color_grid:nav_enable(true, "store_character_hair_color_selected", "store_character_hair_color_nav")
Store_common_color_grid:set_visible(true)
Active_list:set_visible(false)
Current_tier = HAIR_COLOR_TIER
store_character_enable_mouse(false, false)
-- set the titles on top
Store_header:push_title(nil, "CUST_PRIMARY_COLOR")
end
function store_character_hair_color_nav(event, value)
store_character_grid_nav(event, value)
-- set the color and preview the item
local idx = Store_common_color_grid:return_selected_index()
Store_character_nav_value = Color_grid_data[idx]
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
Store_character_nav_thread = thread_new("store_character_delayed_set_hair_item")
end
function store_character_hair_color_selected(event)
-- always make sure to disable inputs, or we'll run out if we start another hair color grid
Store_common_color_grid:nav_enable(false, nil, nil)
-- remove hair color title
Store_header:pop_title()
game_peg_unload("ui_bms_pcr_hair")
Store_common_color_grid:set_visible(false)
Active_list:set_visible(true)
Current_tier = MENU_TIER
store_character_enable_mouse(true, false)
-- Did the player select a color?
if event == "select" or event == "mouse_click" then
game_UI_audio_play("UI_Main_Menu_Select")
if Store_character_nav_thread > -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
local idx = Store_common_color_grid:return_selected_index()
local menu_data = Color_grid_data[idx]
pcr_change_hair(0, menu_data.hair_item, menu_data.primary, 0)
pcr_mark_changed()
store_character_hair_item_save()
store_common_back_menu()
else
game_UI_audio_play("UI_Main_Menu_Nav_Back")
-- reverse the hair color changes since user cancelled, unless they didn't make any changes yet
if Store_character_nav_value ~= nil then
pcr_change_hair(0, Store_character_nav_value.hair_item, Store_character_nav_value.primary, 0)
end
end
Color_grid_data = {}
Store_common_color_grid:cleanup()
end
function store_character_rebuild_hair_menu(menu_data)
-- We need to rebuild the menu to update the accessory color choices available
local menu_data = {}
store_character_fill_hair_menu(menu_data)
Menu_data = {}
Menu_data = Store_common_sub_menu
local index = Active_list:get_selection()
store_character_populate_list(Menu_data, index)
store_character_creation_strip_clothes()
end
function store_character_hair_randomize(menu_data)
-- only head hair uses this now
pcr_hair_randomize_all()
-- randomize facial hair and eyebrows too
pcr_set_random_composite("eyebrows")
-- but don't do facial hair for females
if Store_character_current_preset_gender ~= STORE_CHARACTER_FEMALE_GENDER_NAME then
pcr_set_random_composite("facial hair")
end
-- Need to rebuild menu, so that "hair accessory color" is shown if necessary or not
store_character_rebuild_hair_menu()
end
-- Callback to get a color option from C and add it to our color grid.
--
-- color_index: indicates which color this is for
-- label_crc: crc to use for the toggle choice name. nil if string is used.
-- label_str: string to use for the toggle choice name. nil if crc is used.
-- red: r value.
-- green: g value.
-- blue: b value.
--
function store_character_hair_acc_color_add(color_index, label_crc, label_str, red, green, blue)
local grid_idx = #Color_grid_data + 1
Color_grid_data[grid_idx] = {
icon = "ui_menu_veh_pnt_base",
label = label_str,
label_crc = label_crc,
color = {red = red, green = green, blue = blue},
index = color_index,
}
if color_index == Store_character_hair_acc_info.colors[Color_slot_chosen].index then
Color_grid_idx = grid_idx
end
end
function store_character_hair_acc_color_selected(event)
--Did the player select a color?
if event == "select" or event == "mouse_click" then
game_UI_audio_play("UI_Main_Menu_Select")
-- We don't have to set the colors here... that was already done in our navigate funcion.
else
game_UI_audio_play("UI_Main_Menu_Nav_Back")
-- restore previous color
Store_character_hair_acc_info.colors[Color_slot_chosen].index = Color_grid_data[Color_prev_color].index
Store_character_hair_acc_info.colors[Color_slot_chosen].red = Color_grid_data[Color_prev_color].color.red
Store_character_hair_acc_info.colors[Color_slot_chosen].green = Color_grid_data[Color_prev_color].color.green
Store_character_hair_acc_info.colors[Color_slot_chosen].blue = Color_grid_data[Color_prev_color].color.blue
local t = Store_character_hair_acc_info
pcu_set_slot_info(t.slot_index, t.item_index, t.variant_index, t.colors.num_colors, t.colors[0].index,
t.colors[1].index, t.colors[2].index, t.wear_option_index, t.logo_index, t.logo_color_index, true, CHARACTER_COLOR_POOL_TYPE)
end
store_character_populate_list(Menu_data, Active_list:get_selection())
Store_common_color_grid:nav_enable(false, nil, nil)
Store_common_color_grid:cleanup()
Store_common_color_grid:set_visible(false)
Store_common_color_grid:cleanup()
Active_list:set_visible(true)
Color_grid_data = {}
local t = Store_character_hair_acc_info
t.slot_index, t.item_index, t.item_name_crc, t.item_name_str,
t.num_variants, t.variant_index, t.variant_name_crc, t.variant_name_str,
t.colors.num_colors,
t.colors[0].index, t.colors[0].red, t.colors[0].green, t.colors[0].blue,
t.colors[1].index, t.colors[1].red, t.colors[1].green, t.colors[1].blue,
t.colors[2].index, t.colors[2].red, t.colors[2].green, t.colors[2].blue,
t.num_wear_options, t.wear_option_index, t.wear_option_name_crc, t.wear_option_name_str,
t.num_logos, t.logo_index, t.logo_name_crc, t.logo_name_str, t.logo_color_index, t.logo_color_red, t.logo_color_green, t.logo_color_blue,
t.item_style, t.item_price,
t.player_style_level, t.player_style_percent
= pcu_get_slot_info(HAIR_ITEM_SLOT, CHARACTER_COLOR_POOL_TYPE)
store_character_enable_mouse(true)
Current_tier = MENU_TIER
end
function store_character_hair_acc_color_nav(event, value)
if event == "nav_up" then
Store_common_color_grid:move_cursor(-2)
elseif event == "nav_down" then
Store_common_color_grid:move_cursor(2)
elseif event == "nav_left" then
Store_common_color_grid:move_cursor(-1)
elseif event == "nav_right" then
Store_common_color_grid:move_cursor(1)
elseif event == "mouse_move" then
-- value contains the target_handle in this case
local new_index = Store_common_color_grid:get_data_index(value)
if new_index ~= 0 then
Store_common_color_grid:set_selection(new_index)
Store_common_color_grid:move_cursor(0, false, true)
end
else
--do nothing
end
-- set the color and preview the item
local idx = Store_common_color_grid:return_selected_index()
Store_character_hair_acc_info.colors[Color_slot_chosen].index = Color_grid_data[idx].index
Store_character_hair_acc_info.colors[Color_slot_chosen].red = Color_grid_data[idx].color.red
Store_character_hair_acc_info.colors[Color_slot_chosen].green = Color_grid_data[idx].color.green
Store_character_hair_acc_info.colors[Color_slot_chosen].blue = Color_grid_data[idx].color.blue
local t = Store_character_hair_acc_info
pcu_set_slot_info(t.slot_index, t.item_index, t.variant_index, t.colors.num_colors, t.colors[0].index,
t.colors[1].index, t.colors[2].index, t.wear_option_index, t.logo_index, t.logo_color_index, true, CHARACTER_COLOR_POOL_TYPE)
end
function store_character_hair_acc_color_select(menu_data)
Active_list:set_visible(false)
game_UI_audio_play("UI_Main_Menu_Select")
-- get the colors for the palette
Color_grid_data = {}
Color_slot_chosen = menu_data.id
Color_grid_idx = 1
pcu_report_item_colors("store_character_hair_acc_color_add", 0, CHARACTER_COLOR_POOL_TYPE)
-- display the grid
Store_common_color_grid:draw_items(Color_grid_data, Color_grid_idx, 9, 7, 53.5, 53.5, 15, nil, 1.1, true, true, -7, true)
Store_common_color_grid:set_visible(true)
Store_common_color_grid:nav_enable(true, "store_character_hair_acc_color_selected", "store_character_hair_acc_color_nav")
Color_prev_color = Color_grid_idx
if Mouse_input_tracker ~= 0 then
Mouse_input_tracker:subscribe(false)
end
Current_tier = HAIR_COLOR_TIER
end
function store_character_fill_hair_menu(menu_data)
local menu = {
on_enter = store_character_rebuild_hair_menu,
{ label = "CUST_CAT_HAIR_HEAD", type = TYPE_BUTTON, on_sub_menu_fill = store_character_hair_style_show, rand_func = store_character_hair_randomize, camera = "head" },
{ label = "CUST_CAT_HAIR_FACIAL", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_hair_randomize, composite_area = "facial hair", camera = "facial hair"},
{ label = "CUST_CAT_HAIR_EYEBROWS", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_hair_randomize, composite_area = "eyebrows", camera = "facial hair"},
}
Store_common_sub_menu = menu
local t = Store_character_hair_acc_info
t.slot_index, t.item_index, t.item_name_crc, t.item_name_str,
t.num_variants, t.variant_index, t.variant_name_crc, t.variant_name_str,
t.colors.num_colors,
t.colors[0].index, t.colors[0].red, t.colors[0].green, t.colors[0].blue,
t.colors[1].index, t.colors[1].red, t.colors[1].green, t.colors[1].blue,
t.colors[2].index, t.colors[2].red, t.colors[2].green, t.colors[2].blue,
t.num_wear_options, t.wear_option_index, t.wear_option_name_crc, t.wear_option_name_str,
t.num_logos, t.logo_index, t.logo_name_crc, t.logo_name_str, t.logo_color_index, t.logo_color_red, t.logo_color_green, t.logo_color_blue,
t.item_style, t.item_price,
t.player_style_level, t.player_style_percent
= pcu_get_slot_info(HAIR_ITEM_SLOT, CHARACTER_COLOR_POOL_TYPE)
local menu_idx = #Store_common_sub_menu + 1
-- color selections
if t.colors.num_colors ~= nil and t.colors.num_colors > 0 then
Store_common_sub_menu[menu_idx] = { label = "CUST_HAIR_ACCESSORY_COLOR", type = TYPE_BUTTON, on_sub_menu_fill = store_character_hair_acc_color_menu_fill, rand_func = store_character_hair_randomize, camera = "head" }
end
end
function store_character_hair_acc_color_menu_fill(menu_data)
local menu_idx = #Store_common_sub_menu
local t = Store_character_hair_acc_info
-- color selections
if t.colors.num_colors ~= nil and t.colors.num_colors > 0 then
local c = t.colors
for i = 0, c.num_colors - 1 do
local color_label
if i == 0 then
color_label = "STORE_PRIMARY_COLOR"
elseif i == 1 then
color_label = "STORE_SECONDARY_COLOR"
elseif i == 2 then
color_label = "STORE_TERTIARY_COLOR"
else
color_label = "STORE_DETAIL_COLOR"
end
menu_idx = menu_idx + 1
local new_item = {
type = TYPE_TOGGLE_COLOR,
id = i,
label = color_label,
label_crc = nil,
color = c[i],
on_select = store_character_hair_acc_color_select,
}
Store_common_sub_menu[menu_idx] = new_item
end
end
end
-- Makeup menus
--------------------------------------------
function store_character_makeup_randomize(menu_data)
-- randomize from the makeup selections
local areas = {"makeup eyeliner", "makeup eyeshadow", "makeup cheeks", "makeup entire face", "fingernails", "face items"}
for index, composite_area in pairs(areas) do
pcr_set_random_composite(composite_area)
end
pcr_lips_randomize()
end
function store_character_makeup_enter_menu()
store_character_creation_strip_clothes()
store_set_camera_pos("head")
end
-- Face menus
--------------------------------------------
function store_character_randomize_one_morph(id, name_crc, name_str, value, gender, step_rate, rand_range)
pcr_apply_morph_value(Store_character_randomizing_morph_set, id, rand_float(0.5 - rand_range, 0.5 + rand_range))
pcr_mark_changed()
end
function store_character_randomize_all_morphs(menu_data)
local morph_sets = {"Brow", "Crown", "Forehead", "Eyes", "Nose", "Cheekbones", "Ears", "Chin", "Mouth", "Jaw"}
for index, morph_set_id in pairs(morph_sets) do
Store_character_randomizing_morph_set = morph_set_id
pcr_report_avail_morphs(morph_set_id, "store_character_randomize_one_morph")
end
pcr_randomize_eye_colors()
Store_character_randomizing_morph_set = -1
-- Now remove all facial features, then pick random one(s)
pcr_report_composite_items("face features", "store_character_composite_face_features_remove")
-- super easy algorithm to pick feature, but favor less features over more.
local one_more_chance = 0.6 -- tweak this value to lower or raise expected number of features
local random_features = 0
while random_features < MAX_FACIAL_FEATURES do
if rand_float(0.0, 1.0) > one_more_chance then
-- we're done!
random_features = MAX_FACIAL_FEATURES
else
pcr_set_random_composite("face features", true)
random_features = random_features + 1
end
end
end
function store_character_composite_face_features_remove(composite_index, name_crc, name_str, current, colorized, alpha_select, unused_price, unused_respect)
-- only remove if it's set
if current then
pcr_change_composite("face features", composite_index, -1, -1, COMPOSITE_REMOVE)
end
end
function store_character_randomize_morph_set(menu_data)
for i = 1, #Menu_data do
local curr_item = Menu_data[i]
local v = rand_float(0.5 - curr_item.rand_range, 0.5 + curr_item.rand_range)
curr_item.current_value = v
pcr_apply_morph_value(curr_item.morph_set, curr_item.morph_id, v)
end
-- redraw the sliders with new values
local current_index = Active_list:get_selection()
store_character_populate_list(Menu_data, current_index)
pcr_mark_changed()
end
-- Player accepted morph changes on slider menu
--
function store_character_morph_accept()
game_UI_audio_play("UI_Main_Menu_Select")
store_common_back_menu()
end
-- Player hit cancel on slider menu
--
function store_character_morph_cancel()
-- restore all to original values
for i = 1, #Menu_data do
local curr_item = Menu_data[i]
pcr_apply_morph_value(curr_item.morph_set, curr_item.morph_id, curr_item.orig_value)
end
end
-- Player hit cancel on slider menu - check if we need to confirm with player before proceeding.
-- Return true if a confirm is needed, false if we can proceed.
--
function store_character_morph_cancel_confirm()
if Store_character_cancel_confirm_needed == false then
return false
end
store_character_enable_mouse(false)
Store_common_popup:populate_list(Yes_no_choices, 2, STORE_COMMON_LIST_SIZE, 2)
Store_common_popup:set_title("MENU_TITLE_WARNING")
Store_common_popup:set_text("CANCEL_WARNING_BODY")
Store_common_popup:nav_enable(true, "store_character_morph_cancel_final", "store_character_popup_nav")
Not_popup_grp:set_alpha(.5)
Active_list:set_visible(false)
return true
end
function store_character_morph_cancel_final(event)
Store_common_popup:nav_enable(false, nil, nil)
Not_popup_grp:set_alpha(1)
Active_list:set_visible(true)
-- The user hit the B button and are cancelling the cancel :-)
-- This is redundant to selecting "No" in the list
if event == "back" then
game_UI_audio_play("UI_Main_Menu_Nav_Back")
store_character_enable_mouse(true)
return
end
game_UI_audio_play("UI_Main_Menu_Select")
-- Did we select yes? This assumes yes is the first item
if Store_common_popup:get_selected_data() ~= 1 then
store_character_enable_mouse(true)
return
end
-- Do the cancel
Store_character_cancel_confirm_needed = false
store_common_button_b()
end
function store_character_morph_add(id, name_crc, name_str, value, gender, step_value, rand_range)
local item = {
type = TYPE_SLIDER, morph_id = id, morph_set = Store_character_building_morph_set, rand_func = store_character_randomize_morph_set,
is_gender = gender, on_value_update = store_character_morph_change, label_crc = name_crc, label = name_str,
orig_value = value, current_value = value, min = 0, max = 1, step = step_value, rand_range = rand_range,
on_select = store_character_morph_accept, on_cancel = store_character_morph_cancel, on_cancel_confirm = store_character_morph_cancel_confirm
}
local menu_idx = #Store_common_sub_menu + 1
Store_common_sub_menu[menu_idx] = item
end
function store_character_morph_menu_build(menu_data)
Store_character_building_morph_set = {}
Store_character_building_morph_set = menu_data.morph_set
pcr_report_avail_morphs(menu_data.morph_set, "store_character_morph_add")
Store_character_cancel_confirm_needed = false
if menu_data.camera ~= nil then
store_set_camera_pos(menu_data.camera)
end
end
function store_character_eye_color_add(id, swatch_crc, label_crc, label_str, current)
local menu_idx = #Color_grid_data + 1
local item = {
index = menu_idx, id = id, icon_crc = swatch_crc, label_crc = label_crc, label = label_str, color = {red = 1, green = 1, blue = 1},
}
Color_grid_data[menu_idx] = item
if current == true then
Color_grid_idx = menu_idx
Store_character_orig_eye_color = id
end
end
function store_character_eye_grid_show(menu_data)
-- Disable mouse input for hint buttons
if Hint_bar_mouse_input_tracker ~= -1 then
Hint_bar_mouse_input_tracker:remove_all()
end
--Store_character_original_values.eyes_color = nil
Color_grid_data = {}
Color_grid_idx = 1
pcr_report_eye_colors("store_character_eye_color_add")
-- Load Hair Swatches
game_peg_load_with_cb("store_character_eye_swatch_loaded",1,"ui_bms_pcr_eyes")
store_lock_controls()
store_set_camera_pos("eyes")
-- take randomize choice out of hints
local hint_data = { {CTRL_MENU_BUTTON_B, "MENU_BACK"} }
Store_common_hint_bar:set_hints(hint_data)
-- Re-enable mouse input for hint buttons
if Hint_bar_mouse_input_tracker ~= -1 then
Store_common_hint_bar:add_mouse_inputs("store_character", Hint_bar_mouse_input_tracker)
Hint_bar_mouse_input_tracker:subscribe(true)
end
end
-- Leaving head/face submenu. Currently just makes sure we reposition the camera.
--
function store_character_face_menu_back(menu_data)
store_set_camera_pos("head")
store_character_creation_strip_clothes()
end
function store_character_eye_delayed_set()
delay(0.4)
pcr_change_eye_color(Store_character_eye_selected)
Store_character_nav_value = nil
Store_character_nav_thread = -1
end
function store_character_eye_grid_nav(event, value)
store_character_grid_nav(event, value)
-- set the color and preview the item
local idx = Store_common_color_grid:return_selected_index()
Store_character_eye_selected = Color_grid_data[idx].id
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
Store_character_nav_thread = thread_new("store_character_eye_delayed_set")
end
function store_character_eye_swatch_loaded()
store_unlock_controls()
Store_common_color_grid:draw_items(Color_grid_data, Color_grid_idx, 7, 2, 59, 64, 25)
Store_common_color_grid:nav_enable(true, "store_character_eye_color_selected", "store_character_eye_grid_nav")
Store_common_color_grid:set_visible(true)
Active_list:set_visible(false)
Current_tier = EYE_COLOR_TIER
store_character_enable_mouse(false, false)
-- set the titles on top
Store_header:push_title(nil, "CUST_EYE_COLOR")
end
function store_character_eye_color_selected(event)
-- remove hair color title
Store_header:pop_title()
-- Did the player select a color?
if event == "select" or event == "mouse_click" then
game_UI_audio_play("UI_Main_Menu_Select")
local idx = Store_common_color_grid:return_selected_index()
if idx <= #Color_grid_data then
pcr_change_eye_color(Color_grid_data[idx].id)
pcr_mark_changed()
end
else
if Store_character_nav_thread > -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
pcr_change_eye_color(Store_character_orig_eye_color)
end
game_peg_unload("ui_bms_pcr_eyes")
Store_common_color_grid:nav_enable(false, nil, nil)
Store_common_color_grid:cleanup()
Store_common_color_grid:set_visible(false)
Active_list:set_visible(true)
Color_grid_data = {}
Current_tier = MENU_TIER
store_character_enable_mouse(true, false)
-- restore camera back to head position
store_character_face_menu_back()
-- repopulate menu to restore hints
store_character_populate_list(Menu_data, Active_list:get_selection())
end
-- Personality menus
--------------------------------------------
-- voice
function store_character_set_voice(menu_data)
pcr_set_voice(menu_data.voice, false)
pcr_mark_changed()
-- back out of the menu
store_common_button_b()
end
function store_character_preview_voice(menu_data)
pcr_set_voice(menu_data.voice, true)
end
function store_character_movement_select(menu_data)
pcr_set_anim(menu_data.anim_grp, menu_data.anim_id)
store_common_back_menu()
end
function store_character_movement_revert(menu_data)
pcr_set_anim(menu_data.anim_grp, Store_character_original_anim_id)
end
function store_character_movement_stop()
pcr_preview_anim(nil)
end
function store_character_movement_nav(menu_data)
pcr_preview_anim(menu_data.anim_grp)
pcr_set_anim(menu_data.anim_grp, menu_data.anim_id)
end
function store_character_movement_add_item(display_name, id, current)
local menu_idx = #Store_common_sub_menu + 1
local item = { label = display_name, type = TYPE_BUTTON, anim_id = id, anim_grp = Store_character_movement_anim_grp,
on_nav = store_character_movement_nav, on_select = store_character_movement_select, on_cancel = store_character_movement_revert,
}
if current == true then
Store_character_original_anim_id = id
Store_common_sub_menu.start_index = menu_idx
end
Store_common_sub_menu[menu_idx] = item
end
function store_character_movement_show(menu_data)
Store_common_sub_menu.start_index = 1
Store_character_movement_anim_grp = menu_data.anim_group
pcr_report_anims(menu_data.anim_group, "store_character_movement_add_item")
if #Store_common_sub_menu == 0 then
Store_common_sub_menu[1] = { label = "NO_OPTIONS_RIGHT_NOW", type = TYPE_BUTTON, anim_grp = -1, anim_id = -1 }
else
store_character_movement_nav(Store_common_sub_menu[Store_common_sub_menu.start_index])
end
end
function store_character_voice_menu_fill(menu_data)
local store_character_presets_voice_menu = {
{ label = "CUST_VOICE_MALE_WHITE", type = TYPE_BUTTON, on_nav = store_character_preview_voice, on_select = store_character_set_voice, voice = "MALE_WHITE" },
{ label = "CUST_VOICE_MALE_BLACK", type = TYPE_BUTTON, on_nav = store_character_preview_voice, on_select = store_character_set_voice, voice = "MALE_BLACK" },
{ label = "CUST_VOICE_MALE_LATINO", type = TYPE_BUTTON, on_nav = store_character_preview_voice, on_select = store_character_set_voice, voice = "MALE_WHITE_ACCENT" },
{ label = "CUST_VOICE_FEMALE_WHITE", type = TYPE_BUTTON, on_nav = store_character_preview_voice, on_select = store_character_set_voice, voice = "FEMALE_WHITE" },
{ label = "CUST_VOICE_FEMALE_BLACK", type = TYPE_BUTTON, on_nav = store_character_preview_voice, on_select = store_character_set_voice, voice = "FEMALE_BLACK" },
{ label = "CUST_VOICE_FEMALE_LATINO", type = TYPE_BUTTON, on_nav = store_character_preview_voice, on_select = store_character_set_voice, voice = "FEMALE_LATINO" },
{ label = "CUST_VOICE_ZOMBIE", type = TYPE_BUTTON, on_nav = store_character_preview_voice, on_select = store_character_set_voice, voice = "ZOMBIE" },
}
Store_common_sub_menu = store_character_presets_voice_menu
-- get index of current voice setting; add 1 because lua menu starts at index 1 instead of 0
local selected_voice = pcr_get_voice() + 1
-- check for valid range
if selected_voice >= 1 and selected_voice <= #store_character_presets_voice_menu then
-- set as highlighted option
Store_common_sub_menu.start_index = selected_voice
-- and play a preview of it
store_character_preview_voice(store_character_presets_voice_menu[selected_voice])
end
end
-- Main menu
--------------------------------------------
function store_character_exit_final(event, value)
Store_common_popup:nav_enable(false, nil, nil)
-- The user hit the B button and is exiting
-- This is redundant to selecting "No" in the list
if event == "back" then
game_UI_audio_play("UI_Main_Menu_Nav_Back")
Not_popup_grp:set_alpha(1)
Active_list:set_visible(true)
store_character_enable_mouse(true)
return
end
game_UI_audio_play("UI_Main_Menu_Select")
-- Make sure the popup is set to the correct selection when the user clicks on a button
if event == "mouse_click" then
local new_index = Store_common_popup.list:get_button_index(value)
if new_index ~= 0 then
Store_common_popup.list:set_selection(new_index)
end
end
-- Did we select yes? This assumes yes is the first item
if Store_common_popup:get_selected_data() ~= 1 then
Not_popup_grp:set_alpha(1)
Active_list:set_visible(true)
store_character_enable_mouse(true)
return
end
store_lock_controls()
Not_popup_grp:set_alpha(0)
-- exit the interface. for police lineup, we need to wait for both players in co-op
if pcr_is_plastic_surgeon() == true then
pop_screen()
else
pcr_creation_complete()
end
end
-- Player has requested to leave the store. Get confirmation before doing so.
--
function store_character_exit(menu_data)
local body
local heading = "MENU_TITLE_WARNING"
-- Not allowed to leave store until streaming of character assets is done
if pcu_is_streaming_done() == false then
return
end
if pcr_is_plastic_surgeon() == true then
body = "SURGEON_FINISHED_FULL"
else
body = "CUST_EXIT_WARNING_BODY"
end
store_character_enable_mouse(false)
Store_common_popup:populate_list(Yes_no_choices, 2, STORE_COMMON_LIST_SIZE, 2)
Store_common_popup:set_title(heading)
Store_common_popup:set_text(body)
Store_common_popup:nav_enable(true, "store_character_exit_final", "store_character_popup_nav")
Not_popup_grp:set_alpha(.5)
Active_list:set_visible(false)
end
-- We have returned to the top level menu. Currently restores camera position and
-- makes sure the thread for preset updates is killed. Also returns clothes on the player in creation.
--
function store_character_return_main(menu_data)
store_character_preset_cleanup()
store_set_camera_pos("body")
store_character_creation_add_clothes()
Color_grid_idx = 1
Color_slot_chosen = 0
Color_grid_data = {}
Prev_index = -1
Store_character_randomizing_morph_set = -1
Store_character_building_morph_set = -1
Store_character_preset_previous_settings = -1
Store_character_current_composite_area = -1
Store_character_current_composite_item = -1
Store_character_current_composite_color = -1
Store_character_current_composite_alpha = -1
Store_character_allow_multiple_composites = -1
Store_character_composites_allowed = -1
Store_character_composites_used = -1
Store_character_hair_acc_info = {
colors = { [0] = { }, [1] = { }, [2] = { } }
}
Color_prev_color = 0
Store_character_hair_values = {}
Store_character_skin_color_current_setting = -1
Store_character_skin_color_selected = 0
Store_character_eye_selected = 0
Store_character_movement_anim_grp = -1
Store_character_orig_eye_color = -1
Store_character_strength = -1
Store_character_skinny = -1
Store_character_fat = -1
Store_character_tentative_preset_data = -1
-- kill update thread if it's still around
if Store_character_nav_thread ~= -1 then
thread_kill(Store_character_nav_thread)
Store_character_nav_thread = -1
end
end
function store_character_set_start_camera()
if Store_character_init_camera_moved == false then
store_set_camera_pos("body")
store_start_lighting_change()
Store_character_init_camera_moved = true
end
end
-- Load the online character gallery.
--
function store_character_load_gallery()
Store_character_new_state_on_validation = GALLERY_FULL_STATE
Online_check_thread = thread_new("store_character_check_online_for_gallery")
end
function store_character_check_online_for_gallery()
online_and_privilege_validator_begin(false, true, true, true, true, true) -- Check we're signed in and we have content permissions
while Online_validator_result == ONLINE_VALIDATOR_IN_PROGRESS do
thread_yield()
end
if Online_validator_result ~= ONLINE_VALIDATOR_PASSED then
return
end
if main_menu_check_user_content() == false then
dialog_box_message("MENU_TITLE_WARNING", "USER_CONTENT_PRIV_DENIED")
return
end
pcr_restore_clothing()
-- proceed to the character gallery (could be full menu or download only, based on where we're coming from)
store_character_change_state(Store_character_new_state_on_validation)
Online_check_thread = -1
end
function store_character_face_menu_fill()
local face_menu = {
on_enter = store_character_face_menu_back,
{ label = "CUST_FACIAL_FEATURES", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_randomize_all_morphs, composite_area = "face features", max_selections = MAX_FACIAL_FEATURES, camera = "eyes",},
{ label = "cust_morph_cat_crown", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "crown", camera = "crown",},
{ label = "cust_morph_cat_forehead", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "forehead", camera = "forehead",},
{ label = "cust_morph_cat_brow", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "brow", camera = "brow",},
{ label = "cust_morph_cat_eyes", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "eyes", camera = "eyes",},
{ label = "CUST_EYE_COLOR", type = TYPE_BUTTON, on_select = store_character_eye_grid_show, rand_func = store_character_randomize_all_morphs, on_back = store_character_face_menu_back, camera = "eyes",},
{ label = "cust_morph_cat_ears", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "ears", camera = "ears",},
{ label = "cust_morph_cat_cheekbones", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "cheekbones", camera = "cheekbones",},
{ label = "cust_morph_cat_nose", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "nose", camera = "nose",},
{ label = "cust_morph_cat_mouth", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "mouth", camera = "mouth",},
{ label = "cust_morph_cat_chin", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "chin", camera = "chin",},
{ label = "cust_morph_cat_jaw", type = TYPE_BUTTON, on_sub_menu_fill = store_character_morph_menu_build, rand_func = store_character_randomize_all_morphs, morph_set = "jaw", camera = "jaw",},
}
Store_common_sub_menu = face_menu
end
function store_character_makeup_menu_fill()
local makeup_menu = {
on_enter = store_character_makeup_enter_menu,
{ label = "CUST_EYELINER", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_makeup_randomize, composite_area = "makeup eyeliner", camera = "eyes", },
{ label = "CUST_EYESHADOW", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_makeup_randomize, composite_area = "makeup eyeshadow", camera = "eyes", },
{ label = "CUST_CAT_MAKEUP_CHEEKS", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_makeup_randomize, composite_area = "makeup cheeks", camera = "head", },
{ label = "CUST_CAT_MAKEUP_MOUTH", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_makeup_randomize, composite_area = "lips", camera = "head" },
{ label = "CUST_CAT_MAKEUP_ENTIRE_FACE", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_makeup_randomize, composite_area = "makeup entire face", camera = "head", },
{ label = "CUST_FACIAL_ITEMS", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_makeup_randomize, composite_area = "face items", camera = "entire face", },
{ label = "CUST_CAT_FINGERNAILS", type = TYPE_BUTTON, on_sub_menu_fill = store_character_composite_menu_build, rand_func = store_character_makeup_randomize, composite_area = "fingernails", camera = "hand left", },
}
Store_common_sub_menu = makeup_menu
end
function store_character_personality_menu_fill()
local personality_menu = {
on_enter = store_character_movement_stop,
{ label = "CUST_VOICE", type = TYPE_BUTTON, on_sub_menu_fill = store_character_voice_menu_fill },
{ label = "CUST_MOVEMENT_COMPLIMENT", type = TYPE_BUTTON, on_sub_menu_fill = store_character_movement_show, anim_group = "compliment_style", },
{ label = "CUST_MOVEMENT_TAUNT", type = TYPE_BUTTON, on_sub_menu_fill = store_character_movement_show, anim_group = "taunt_style", },
}
Store_common_sub_menu = personality_menu
end
function store_character_main_menu_fill()
local main_menu = {
on_enter = store_character_return_main,
{ label = "CUST_MENU_GLOBAL", type = TYPE_BUTTON, id = 2, on_sub_menu_fill = store_character_fill_global_menu, camera = "body", },
{ label = "CUST_MENU_FINE_TUNING", type = TYPE_BUTTON, id = 3, on_sub_menu_fill = store_character_face_menu_fill, camera = "head", },
{ label = "CUST_MENU_HAIR", type = TYPE_BUTTON, id = 4, on_sub_menu_fill = store_character_fill_hair_menu, camera = "head", },
{ label = "CUST_MENU_MAKEUP", type = TYPE_BUTTON, id = 5, on_sub_menu_fill = store_character_makeup_menu_fill, camera = "head", },
{ label = "CUST_MENU_MOVEMENT", type = TYPE_BUTTON, id = 6, on_sub_menu_fill = store_character_personality_menu_fill, camera = "body", },
{ label = "STORE_CHARACTER_GALLERY", type = TYPE_BUTTON, on_select = store_character_load_gallery, },
{ label = "STORE_EXIT", type = TYPE_BUTTON, id = 7, on_select = store_character_exit }
}
Menu_data = main_menu
end
---------------------------------------------------------------------------
-- Initialize Character Store
---------------------------------------------------------------------------
function store_character_init()
if pcr_is_plastic_surgeon() then
Store_common_current_highlight_color = COLOR_STORE_CHARACTER_PRIMARY
else
Store_common_current_highlight_color = COLOR_SAINTS_PURPLE
end
Game_platform = game_get_platform()
-- unload the full screen map to pillage memory
pause_map_dump()
-- Find doc handle
Store_character_doc_handle = vint_document_find("store_character")
-- Set up some callbacks for the common store script
Store_common_allow_input_cb = store_character_allow_input
Store_common_populate_list_cb = store_character_populate_list
Store_common_exit_cb = store_character_exit
-- Subscribe to the button presses we need, Input_tracker is created in store_common.lua
Input_tracker:add_input("alt_select", "store_character_confirm_randomize", 50)
store_lock_controls()
if Game_platform == "PC" then
Hint_bar_mouse_input_tracker = Vdo_input_tracker:new()
end
--Setup Button Hints
local hint_data = {
{CTRL_MENU_BUTTON_B, "MENU_BACK"},
}
Store_common_hint_bar:set_hints(hint_data)
Store_common_hint_bar:set_visible(true)
if game_is_active_input_gamepad() then
local hint_rotate_data = {
{CTRL_BUTTON_RS, "STORE_ZOOM_ROTATE"},
}
Store_common_rotate_hint:set_hints(hint_rotate_data)
Store_common_rotate_hint:enable_text_shadow(true)
if game_get_platform() ~= "PC" then
Store_common_rotate_hint:set_visible(true)
end
end
Store_header:set_visible(true)
-- Everything that is not in the popup so we can fade it when the popup is active
Not_popup_grp = Vdo_base_object:new("not_popup_grp", 0, Store_common_doc_handle)
-- Setup purchase popup
Store_common_popup:set_visible(false)
Store_common_popup:set_size(STORE_COMMON_LIST_SIZE, (LIST_BUTTON_HEIGHT * 5) + (LIST_BUTTON_SPACE * 5))
-- Setup Color grid
Store_common_color_grid:set_visible(false)
Store_common_color_grid:set_ui_screen("store_character")
-- Setup Triangle Select
Triangle_select = Vdo_triangle_select:new("store_character_triangle", 0, Store_character_doc_handle)
Triangle_select:set_visible(false)
-- Populate the areas and draw the first menu
store_character_main_menu_fill()
local is_plastic_surgeon = pcr_is_plastic_surgeon()
Store_character_confirm_randomize = is_plastic_surgeon
-- If we're in the character lineup, change the text on the "exit" choice
if is_plastic_surgeon == false then
Menu_data[#Menu_data].label = "CUST_MENU_EXIT"
Menu_data[#Menu_data].is_new = true
Active_list:set_highlight_color(COLOR_SAINTS_PURPLE, COLOR_PLAYER_CREATION_SECONDARY, COLOR_PLAYER_CREATION_TERTIARY)
end
-- Pass the table data to the megalist vdo.
store_character_populate_list(Menu_data, 1)
-- Hide background animations
local player_creation_grp = Vdo_base_object:new("player_creation_grp", 0, Store_character_doc_handle)
local iad_bg_grp = Vdo_base_object:new("iad_bg_grp", 0, Store_character_doc_handle)
player_creation_grp:set_visible(false)
player_creation_grp:set_property("background", false)
iad_bg_grp:set_visible(false)
local color_primary, color_secondary, color_tertiary
--setup common anims and tweens to both stores
Store_character_in_anim = Vdo_anim_object:new("store_common_in_anim", 0, Store_common_doc_handle)
local list_x, list_y = Active_list:get_anchor()
local grid_x, grid_y = Store_common_color_grid:get_anchor()
Active_list:set_anchor(list_x, STORE_COMMON_LIST_NO_PRICE_Y)
Store_common_color_grid:set_anchor(grid_x, STORE_COMMON_GRID_NO_PRICE_Y)
-- reset this variable since we might be coming from IAD
Store_lineup_is_loaded = true
-- What store are we in?
if is_plastic_surgeon == false then
Store_character_current_state = LINEUP_STATE
--Handle input for character line-up component
-- Gets rid of the repsect meter in player creation
Store_header:reformat_and_resize(true,435)
-- Change colors for player creation
color_primary = COLOR_SAINTS_PURPLE
--color_primary = COLOR_PLAYER_CREATION_PRIMARY
color_secondary = COLOR_PLAYER_CREATION_SECONDARY
color_tertiary = COLOR_PLAYER_CREATION_TERTIARY
-- Set background animations
Store_character_out_anim = Vdo_anim_object:new("store_common_anim_out", 0, Store_common_doc_handle)
Player_creation_bg_anim = Vdo_anim_object:new("player_creation_bg_anim", 0, Store_character_doc_handle)
vint_apply_start_values(Store_character_in_anim.handle)
vint_apply_start_values(Player_creation_bg_anim.handle)
local twn_h = vint_object_find("new_tween1", Player_creation_bg_anim.handle)
vint_set_property(twn_h, "end_event", "store_character_bg_anim_done")
-- MWA - make call to unload the city loading bitmaps - there is weird case where
-- we enter customization screen right as city is done loading and we need to unload them
-- Honnigford fixed this, but they were unloading too early. I'm moving the unload to
-- be immediately before we need screen.
--local city_load_doc_h = vint_document_find( "city_load" )
--if city_load_doc_h ~= 0 then
-- city_load_hide_images( )
--end
game_peg_load_with_cb("store_character_line_up_is_loaded", 1, "ui_bms_store_line_up")
Store_lineup_is_loaded = false
Active_list:set_visible(false)
Store_header:set_visible(false)
Store_common_rotate_hint:set_visible(false)
vint_set_property(vint_object_find("police_logo"), "visible", false)
-- Change the top level exit callback so that we go back to the character lineup instead of the game.
Store_common_exit_cb = store_character_lineup_return_confirm
-- Change hint text
local hint_data = {
{CTRL_MENU_BUTTON_A, "CUST_CHOOSE_CHARACTER"},
}
Store_common_hint_bar:set_hints(hint_data)
character_enable_mouse_drag(false)
else
Store_character_current_state = CUSTOMIZATION_STATE
-- Change colors for store character
color_primary = COLOR_STORE_CHARACTER_PRIMARY
color_secondary = COLOR_STORE_CHARACTER_SECONDARY
color_tertiary = COLOR_STORE_CHARACTER_TERTIARY
-- Set animations
Iad_bg_anim = Vdo_anim_object:new("iad_bg_anim", 0, Store_character_doc_handle)
Iad_logo_anim = Vdo_anim_object:new("iad_logo_anim", 0, Store_character_doc_handle)
-- Set tweens
local end_event_twn = Vdo_tween_object:new("end_event_twn", Store_character_in_anim.handle)
end_event_twn:set_end_event("store_unlock_controls")
local iad_end_event_twn = Vdo_tween_object:new("iad_bg_end_event",Iad_bg_anim.handle,Store_character_doc_handle)
iad_end_event_twn:set_end_event("store_loop_bg_anims")
local twn_h = vint_object_find("new_tween23", Iad_bg_anim.handle)
vint_set_property(twn_h, "end_event", "store_character_bg_anim_done")
vint_apply_start_values(Iad_bg_anim.handle)
vint_apply_start_values(Store_character_in_anim.handle)
vint_apply_start_values(Iad_logo_anim.handle)
ui_audio_post_event("enter_image_as_designed")
-- Stream store graphic then play animation in callback
game_peg_load_with_cb("store_character_bg_is_loaded", 1, "ui_bms_store_iad")
character_enable_mouse_drag(true)
end
-- Set colors
Store_header:set_color(color_primary, color_secondary, color_tertiary)
Active_list:set_highlight_color(color_primary, color_secondary, color_tertiary)
Store_common_color_grid:set_highlight_color(color_primary, color_secondary, color_tertiary)
Triangle_select:set_highlight_color(color_primary, color_secondary, color_tertiary)
Store_common_popup:set_color(color_primary, color_secondary, color_tertiary)
-- Set initial titles
if pcr_is_plastic_surgeon() == false then
Store_header:push_title(nil, "STORE_CHOOSE_A_CATEGORY")
else
Store_header:set_title()
end
end
function store_lock_controls()
Input_tracker:subscribe(false)
store_character_enable_mouse(false)
if Input_tracker_body_morph_select ~= nil then
Input_tracker_body_morph_select:subscribe(false)
end
end
function store_unlock_controls()
Input_tracker:subscribe(true)
if Current_tier == BUILD_TIER then
if Input_tracker_body_morph_select ~= nil then
Input_tracker_body_morph_select:subscribe(true)
end
if Hint_bar_mouse_input_tracker ~= -1 then
Hint_bar_mouse_input_tracker:subscribe(true)
end
else
store_character_enable_mouse(true)
end
end
function store_loop_bg_anims(tween_handle)
local animation = vint_object_parent(tween_handle)
-- Loop bg anim starting at frame 40
lua_play_anim(animation, -1)
end
-- Callback for when background and related images for lineup are loaded.
--
function store_character_line_up_is_loaded()
local player_creation_grp = Vdo_base_object:new("player_creation_grp", 0, Store_character_doc_handle)
player_creation_grp:set_visible(true)
image_streamer_add_image("ui_store_lineup_mugshot_01")
image_streamer_add_image("ui_store_lineup_mugshot_02")
image_streamer_add_image("ui_store_lineup_mugshot_03")
image_streamer_add_image("ui_store_lineup_mugshot_04")
image_streamer_add_image("ui_store_lineup_mugshot_05")
image_streamer_add_image("ui_store_lineup_mugshot_06")
image_streamer_add_image("ui_store_lineup_mugshot_07")
image_streamer_add_image("ui_store_lineup_mugshot_08")
image_streamer_add_image("ui_store_lineup_mugshot_09")
local susan = Vdo_susan:new("susan", 0, Store_character_doc_handle, "store_character.lua", "Susan_object")
Susan_object = susan
Susan_object:set_visible(false)
image_streamer_start_streaming(STORE_CHARACTER_NUM_LINEUP_IMAGES - 1, STORE_CHARACTER_NUM_LINEUP_IMAGES_LOADED, store_character_lineup_images_loaded)
-- Turn off customization inputs
store_lock_controls()
-- subscribe to inputs for the character lineup
Lineup_input_tracker = Vdo_input_tracker:new()
Lineup_input_tracker:add_input("nav_left", "store_character_lineup_nav", 50)
Lineup_input_tracker:add_input("nav_right", "store_character_lineup_nav", 50)
Lineup_input_tracker:add_input("select", "store_character_lineup_select", 50)
Lineup_input_tracker:subscribe(true)
Susan_mouse_area_img_h = vint_object_find("susan_mouse_area_img", 0 ,Store_character_doc_handle)
if game_get_platform() == "PC" then
Lineup_mouse_input_tracker = Vdo_input_tracker:new()
Lineup_mouse_input_tracker:add_mouse_input("mouse_click", "store_character_mouse_click", 25, Susan_mouse_area_img_h)
Lineup_mouse_input_tracker:add_mouse_input("mouse_click", "store_character_mouse_click", 50, Susan_object.arrow_left.handle)
Lineup_mouse_input_tracker:add_mouse_input("mouse_click", "store_character_mouse_click", 50, Susan_object.arrow_right.handle)
Lineup_mouse_input_tracker:add_mouse_input("mouse_move", "store_character_mouse_move", 50, Susan_object.arrow_left.handle)
Lineup_mouse_input_tracker:add_mouse_input("mouse_move", "store_character_mouse_move", 50, Susan_object.arrow_right.handle)
Lineup_mouse_input_tracker:subscribe(true)
end
store_character_enable_mouse_split(false, true, true)
Current_tier = LINEUP_TIER
Store_lineup_is_loaded = true
end
-- Callback for when lineup images are done loading.
--
function store_character_lineup_images_loaded()
if Store_character_lineup_init_complete == false then
Store_character_in_anim:play(0)
Player_creation_bg_anim:play(0)
Susan_object:set_images(image_streamer_get_images())
Susan_object:play_intro()
Susan_object:set_visible(true)
Store_character_lineup_init_complete = true
store_character_lineup_loaded()
end
end
function store_character_is_lineup_loaded()
return Store_lineup_is_loaded
end
function store_character_bg_is_loaded()
Store_character_in_anim:play(0)
Iad_bg_anim:play(0)
Iad_logo_anim:play(0)
-- Unhide background
local iad_bg_grp = Vdo_base_object:new("iad_bg_grp", 0, Store_character_doc_handle)
iad_bg_grp:set_visible(true)
end
---------------------------------------------------------------------------
-- Cleanup Character Store
---------------------------------------------------------------------------
function store_character_cleanup()
-- we needed to add a second temporary persona to the player in pcr_set_voice. This needs to be cleared out on exit
pcr_cleanup_player_voice()
-- Nuke all button subscriptions
store_lock_controls()
game_peg_unload("ui_bms_pcr_tri")
game_peg_unload("ui_bms_pcr_skin")
game_peg_unload("ui_bms_pcr_eyes")
game_peg_unload("ui_bms_pcr_hair")
game_peg_unload("ui_bms_store_iad")
game_peg_unload("ui_bms_store_line_up")
-- Remove any images from the character lineup
image_streamer_end_streaming()
image_streamer_remove_all_images()
-- reload the full screen map
pause_map_restore()
character_enable_mouse_drag(false)
-- Some things need to be cleaned up right away when a store doc is unloading, particular disabled inputs which have
-- callbacks to the script that is unloading.
store_common_cleanup_current_store()
if Online_check_thread ~= -1 then
thread_kill(Online_check_thread)
end
if Store_character_clothing_load_thread ~= -1 then
thread_kill(Store_character_clothing_load_thread)
end
end
-- Populate the active megalist menu, and hide the previous menu
--
-- list_data: this table contains the data to populate the megalist
-- current_index: the current index in the menu that's selected
--
function store_character_populate_list(list_data, current_index)
Active_list:draw_items(list_data, current_index, STORE_COMMON_LIST_SIZE, 12, SCALE_FONT_STORE_CHARACTER_LIST)
Active_list:set_visible(true)
-- Update the hint bar
-- Are we on the main screen?
local back_hint
if store_common_stack_get_menu() ~= nil then
back_hint = {CTRL_MENU_BUTTON_B, "MENU_BACK"}
else
-- Are we in plastic surgeon or character creation?
if pcr_is_plastic_surgeon() then
back_hint = {CTRL_MENU_BUTTON_B, "STORE_EXIT"}
else
back_hint = {CTRL_MENU_BUTTON_B, "MENU_BACK"}
end
end
-- build hint data
local hint_data = {}
local hint_count = 0
-- If all the items in the menu are sliders (no submenus) then add an "accept" hint
-- restore all to original values
local has_buttons = false
for i = 1, #list_data do
if list_data[i].type == TYPE_BUTTON or list_data[i].type == TYPE_TOGGLE_COLOR then
has_buttons = true
end
end
if has_buttons == false then
hint_count = hint_count + 1
hint_data[hint_count] = {CTRL_MENU_BUTTON_A, "PAUSE_MENU_ACCEPT"}
end
-- add back choice
hint_count = hint_count + 1
hint_data[hint_count] = back_hint
-- Is there a randomize function?
if list_data[1].rand_func ~= nil then
hint_count = hint_count + 1
hint_data[hint_count] = {CTRL_BUTTON_X, "PLAYER_CREATION_RANDOMIZE"}
end
Store_common_hint_bar:set_hints(hint_data)
if Game_platform == "PC" then
Mouse_input_tracker:remove_all()
Hint_bar_mouse_input_tracker:remove_all()
Active_list:set_store("store_character")
Active_list:add_mouse_inputs("store_common", Mouse_input_tracker)
Store_common_hint_bar:add_mouse_inputs("store_character", Hint_bar_mouse_input_tracker)
store_character_enable_mouse(true)
end
end
-- Callback that's called when asynchronous image loading is complete
--
function store_character_set_logo()
-- TODO
end
-- Handle navigating grid
function store_character_grid_nav(event, value)
if event == "nav_up" then
Store_common_color_grid:move_cursor(-2)
elseif event == "nav_down" then
Store_common_color_grid:move_cursor(2)
elseif event == "nav_left" then
Store_common_color_grid:move_cursor(-1)
elseif event == "nav_right" then
Store_common_color_grid:move_cursor(1)
elseif event == "mouse_move" then
-- value contains the target_handle in this case
local new_index = Store_common_color_grid:get_data_index(value)
if new_index ~= 0 then
Store_common_color_grid:set_selection(new_index)
Store_common_color_grid:move_cursor(0, false, true)
end
else
--do nothing
end
end
function store_character_confirm_randomize()
-- Is the active list visible?
if Active_list:get_visible() == false then
return
end
-- Does a randomization function exist for this data?
local current_index = Active_list:get_selection()
if Menu_data[current_index].rand_func == nil then
return
end
-- if we don't need to warn the player first, just randomize
if Store_character_confirm_randomize == false then
game_UI_audio_play("UI_Main_Menu_Select")
-- Do the randomization
local current_index = Active_list:get_selection()
Menu_data[current_index].rand_func(Menu_data[current_index])
return
end
store_character_enable_mouse(false)
Store_common_popup:populate_list(Yes_no_choices, 2, STORE_COMMON_LIST_SIZE, 2)
Store_common_popup:set_title("RANDOM_WARNING_HEADING")
Store_common_popup:set_text("RANDOM_WARNING_BODY")
Store_common_popup:nav_enable(true, "store_character_randomize_final", "store_character_popup_nav")
Not_popup_grp:set_alpha(.5)
Active_list:set_visible(false)
end
function store_character_randomize_final(event)
Store_common_popup:nav_enable(false, nil, nil)
Not_popup_grp:set_alpha(1)
Active_list:set_visible(true)
-- The user hit the B button and are cancelling the purchase
-- This is redundant to selecting "No" in the list
if event == "back" then
game_UI_audio_play("UI_Main_Menu_Nav_Back")
store_character_enable_mouse(true)
return
end
game_UI_audio_play("UI_Main_Menu_Select")
-- Did we select yes? This assumes yes is the first item
if Store_common_popup:get_selected_data() ~= 1 then
store_character_enable_mouse(true)
return
end
-- Do the randomization
Store_character_confirm_randomize = false
local current_index = Active_list:get_selection()
Menu_data[current_index].rand_func(Menu_data[current_index])
store_character_enable_mouse(true)
-- We'll now need to confirm if they want to throw the changes out
Store_character_cancel_confirm_needed = true
end
function store_character_popup_nav(event, value)
if event == "mouse_move" then
vint_set_mouse_cursor("")
end
if event == "nav_up" then
Store_common_popup.list:move_cursor(-1)
elseif event == "nav_down" then
Store_common_popup.list:move_cursor(1)
elseif event == "mouse_move" then
-- value contains the target_handle in this case
local new_index = Store_common_popup.list:get_button_index(value)
if new_index ~= 0 then
Store_common_popup.list:set_selection(new_index)
Store_common_popup.list:move_cursor(0, true)
end
else
--do nothing
end
end
function store_character_play_exit_anim()
ui_audio_post_event("UI_Store_Exit")
local anim_out = Vdo_anim_object:new("iad_anim_out", 0, Store_character_doc_handle)
local end_event_twn = Vdo_tween_object:new("iad_end_event", anim_out.handle)
end_event_twn:set_end_event("store_unload_doc")
anim_out:play(0)
store_common_play_anim_out()
end
function store_unload_doc()
vint_document_unload()
end
-- =====================================
-- Mouse Specific Functions
-- =====================================
-- Subscribes or removes subscriptions to all mouse input trackers
--
function store_character_enable_mouse(enable, set_hint_bar)
if Game_platform == "PC" and enable ~= nil then
if Mouse_input_tracker ~= 0 then
Mouse_input_tracker:subscribe(enable)
end
Active_list:enable_toggle_input(enable)
-- The hint bar is defaulted to be subscribed with the other input trackers unless specified
if set_hint_bar == nil then
set_hint_bar = true
end
if Hint_bar_mouse_input_tracker ~= -1 and set_hint_bar then
Hint_bar_mouse_input_tracker:subscribe(enable)
end
end
end
-- Subscribes or removes subscriptions to specific mouse input trackers
--
function store_character_enable_mouse_split(enable_active_list, enable_hint_bar, enable_lineup)
if Game_platform == "PC" then
if Mouse_input_tracker ~= nil then
Mouse_input_tracker:subscribe(enable_active_list)
end
if Hint_bar_mouse_input_tracker ~= -1 then
Hint_bar_mouse_input_tracker:subscribe(enable_hint_bar)
end
if Lineup_mouse_input_tracker ~= nil then
Lineup_mouse_input_tracker:subscribe(enable_lineup)
end
end
end
function store_character_mouse_click(event, target_handle, mouse_x, mouse_y)
-- Check if the user clicked on the hint bar
local hint_index = Store_common_hint_bar:get_hint_index(target_handle)
if hint_index ~= 0 then
-- Depending on the current tier, clicking on the "Back" button on the hint bar needs
-- to call different functions
if Store_common_hint_bar.hint_data[hint_index][1] == CTRL_BUTTON_X then
store_character_confirm_randomize()
elseif Current_tier == LINEUP_TIER and Store_character_current_state == LINEUP_STATE then
store_character_lineup_select(event)
elseif Store_common_hint_bar.hint_data[hint_index][1] == CTRL_MENU_BUTTON_B then
if Current_tier == BUILD_TIER then
store_character_morph_revert_confirm()
elseif Current_tier == SKIN_COLOR_TIER then
store_character_skin_color_selected("back")
elseif Current_tier == EYE_COLOR_TIER then
store_character_eye_color_selected("back")
elseif Current_tier == HAIR_COLOR_TIER then
store_character_hair_color_selected("back")
elseif Current_tier == MAKEUP_COLOR_TIER then
store_character_composite_color_select("back")
else --Current tier == MENU_TIER
store_common_button_b()
end
elseif Store_common_hint_bar.hint_data[hint_index][1] == CTRL_MENU_BUTTON_A then
store_common_button_a()
end
return
end
-- Check if the user clicked on a susan arrow
if Susan_object ~= nil and Susan_object ~= -1 then
if target_handle == Susan_object.arrow_left.handle then
-- Navigate left
store_character_lineup_nav("nav_left")
elseif target_handle == Susan_object.arrow_right.handle then
-- Navigate right
store_character_lineup_nav("nav_right")
elseif target_handle == Susan_mouse_area_img_h then
-- Select character from lineup
if Current_tier == LINEUP_TIER then
store_character_lineup_select(event)
end
end
end
-- Check if the user clicked on the confirm button or triangle on the build screen
if Left_stick_input_thread ~= -1 and Triangle_select_button_handle == target_handle then
store_character_morph_select()
end
local triangle_handle = vint_object_find("triangle_base", Triangle_select.handle)
if Left_stick_input_thread ~= -1 and triangle_handle == target_handle then
Mouse_x = mouse_x
Mouse_y = mouse_y
end
end
function store_character_mouse_move(event, target_handle)
vint_set_mouse_cursor("")
store_common_mouse_zoom(0.0)
-- Reset highlights
Store_common_hint_bar:set_highlight(0)
Active_list:highlight_left_arrow(false)
Active_list:highlight_right_arrow(false)
-- Check if the user is over the hint bar
local hint_index = Store_common_hint_bar:get_hint_index(target_handle)
if hint_index ~= 0 then
if pcr_is_plastic_surgeon() then
Store_common_hint_bar:set_highlight(hint_index, Store_common_current_highlight_color)
else
Store_common_hint_bar:set_highlight(hint_index, COLOR_SAINTS_PURPLE) --COLOR_PLAYER_CREATION_PRIMARY)
end
end
-- Check if the user is over a button on the list
local old_index = Active_list:get_selection()
local new_index = Active_list:get_button_index(target_handle)
if new_index ~= 0 and new_index ~= old_index then
Active_list:set_selection(new_index)
Active_list:move_cursor(0, true)
-- If the item has a callback for navigation, do it (such as putting clothes on)
local data_item = Active_list:return_selected_data()
if data_item.on_nav ~= nil then
data_item.on_nav(data_item)
end
end
-- Check if the user is over the toggle/slider arrows
local current_selection = Active_list:return_selected_data()
if current_selection.type == TYPE_SLIDER then
if Active_list:is_left_arrow(target_handle) then
Active_list:highlight_left_arrow(true)
elseif Active_list:is_right_arrow(target_handle) then
Active_list:highlight_right_arrow(true)
end
end
-- Custom highlighting for the select build button
vint_set_property(Triangle_select_text_handle, "tint", 0, 0, 0)
if Left_stick_input_thread ~= -1 and Triangle_select_button_handle == target_handle then
vint_set_property(Triangle_select_text_handle, "tint", 220/255, 220/255, 220/255)
end
local SUSAN_ARROW_SCALE = 1.5
-- Custom highlighting for the lineup arrows
if Susan_object ~= nil and Susan_object ~= -1 then
Susan_object.arrow_left:set_scale(SUSAN_ARROW_SCALE, SUSAN_ARROW_SCALE)
Susan_object.arrow_right:set_scale(SUSAN_ARROW_SCALE, SUSAN_ARROW_SCALE)
if target_handle == Susan_object.arrow_left.handle then
Susan_object.arrow_left:set_scale(SUSAN_ARROW_SCALE * 1.15, SUSAN_ARROW_SCALE * 1.15)
elseif target_handle == Susan_object.arrow_right.handle then
Susan_object.arrow_right:set_scale(SUSAN_ARROW_SCALE * 1.15, SUSAN_ARROW_SCALE * 1.15)
end
end
end
function store_character_mouse_scroll(event, target_handle, mouse_x, mouse_y, scroll_lines)
if scroll_lines ~= 0 then
if Store_common_color_grid.bg.handle == target_handle then
Store_common_color_grid:scroll_list(scroll_lines * -1)
end
end
end
function store_character_mouse_drag(event, target_handle, mouse_x, mouse_y)
if Store_common_color_grid.scrollbar.tab.handle == target_handle then
local new_start_index = Store_common_color_grid.scrollbar:drag_scrolltab(mouse_y, Store_common_color_grid.rows - (Store_common_color_grid.max_height - 1))
Store_common_color_grid:scroll_list(0, new_start_index)
return
end
-- Set the mouse's position to start the update
local triangle_handle = vint_object_find("triangle_base", Triangle_select.handle)
if Left_stick_input_thread ~= -1 and triangle_handle == target_handle then
Mouse_x = mouse_x
Mouse_y = mouse_y
end
end
function store_character_mouse_drag_release(event, target_handle, mouse_x, mouse_y)
-- Rebuild the mouse tracker and snap the scrolltab to the index's notch
if Store_common_color_grid.scrollbar.tab.handle == target_handle then
local start_row = Store_common_color_grid:get_visible_rows()
Store_common_color_grid.scrollbar:release_scrolltab(start_row, Store_common_color_grid.rows - (Store_common_color_grid.max_height - 1))
Store_common_color_grid:update_mouse_inputs()
return
end
-- Reset the mouse's tracked position, so the build screen doesn't constantly update
local triangle_handle = vint_object_find("triangle_base", Triangle_select.handle)
if triangle_handle == target_handle then
Mouse_x = -1
Mouse_y = -1
end
end
-- Navigate left and right events are handled for the character lineup here.
--
-- event: only accepts left and right navs.
--
function store_character_lineup_nav(event)
if event == "nav_left" then
if not image_streamer_is_stream_in_progress() and not Susan_object:is_in_progress() and Susan_object:get_index() > 1 then
Susan_object:move_right()
game_UI_audio_play("UI_Main_Menu_Nav_Page_Right")
if image_streamer_nav_index(-1) == false then
assert_msg(false, "nav_left failed")
end
end
elseif event == "nav_right" then
if not image_streamer_is_stream_in_progress() and not Susan_object:is_in_progress() and Susan_object:get_index() < STORE_CHARACTER_NUM_LINEUP_IMAGES then
Susan_object:move_left()
game_UI_audio_play("UI_Main_Menu_Nav_Page_Right")
if image_streamer_nav_index(1) == false then
assert_msg(false, "nav_right failed")
end
end
else
--do nothing
end
local index = Susan_object:get_index()
if index == STORE_CHARACTER_NUM_LINEUP_IMAGES then
if index ~= Prev_index then
Susan_object:show_text("LINEUP_DOWNLOAD_CHARACTER", "LINEUP_DOWNLOAD_CHARACTER_DESC", true)
end
else
Susan_object:show_text(nil, nil, false)
end
Prev_index = index
end
-- The player has selected a picture from the character lineup, and this function will take us to the character customization screen.
--
function store_character_lineup_select(event)
local new_state
local index = Susan_object:get_index()
Store_character_current_state = LINEUP_STATE
-- Special case: the last choice is for the Agora character gallery choice
if index == STORE_CHARACTER_NUM_LINEUP_IMAGES then
Store_character_new_state_on_validation = GALLERY_DOWNLOAD_STATE
Online_check_thread = thread_new("store_character_check_online_for_gallery")
else
store_character_change_state(CUSTOMIZATION_STATE)
end
end
-- Player selected a preset with the "select" button. We need to confirm if they really want to do this first.
--
function store_character_lineup_return_confirm()
store_character_enable_mouse(false)
Store_common_popup:populate_list(Yes_no_choices, 2, STORE_COMMON_LIST_SIZE, 2)
Store_common_popup:set_title("MENU_TITLE_WARNING")
Store_common_popup:set_text("LINEUP_WARNING_BODY")
Store_common_popup:nav_enable(true, "store_character_lineup_return_confirm2", "store_character_popup_nav")
Not_popup_grp:set_alpha(.5)
Active_list:set_visible(false)
return true
end
function store_character_lineup_return_confirm2(event)
Store_common_popup:nav_enable(false, nil, nil)
Not_popup_grp:set_alpha(1)
Active_list:set_visible(true)
-- The user hit the B button and cancelled the change
-- This is redundant to selecting "No" in the list
if event == "back" then
game_UI_audio_play("UI_Main_Menu_Nav_Back")
store_character_enable_mouse(true)
return
end
game_UI_audio_play("UI_Main_Menu_Select")
-- Did we select yes? This assumes yes is the first item
if Store_common_popup:get_selected_data() ~= 1 then
store_character_enable_mouse(true)
return
end
-- return to the lineup
store_character_change_state(LINEUP_STATE)
end
function store_character_lineup_susan_in_done()
Store_character_anim_done = true
Lineup_input_tracker:subscribe(true)
store_character_enable_mouse_split(false, true, true)
Current_tier = LINEUP_TIER
end
function store_character_lineup_susan_out_done()
Store_character_anim_done = true
Input_tracker:subscribe(true)
store_character_enable_mouse_split(true, true, false)
Current_tier = MENU_TIER
image_streamer_end_streaming()
end
function store_character_allow_input()
return Store_character_anim_done
end
-- Called when this interface becomes the top one on the stack again.
--
function store_character_gained_focus()
local is_plastic_surgeon = pcr_is_plastic_surgeon()
local new_state
if Store_character_current_state == GALLERY_DOWNLOAD_STATE and Store_common_gallery_cancel then
new_state = LINEUP_STATE
else
new_state = CUSTOMIZATION_STATE
character_enable_mouse_drag(true)
end
Store_common_gallery_cancel = false
store_character_change_state(new_state)
if is_plastic_surgeon == false and Store_common_gallery_download_only == true then
vint_set_property(vint_object_find("police_logo"), "visible", true)
end
if Hint_bar_mouse_input_tracker ~= -1 then
Hint_bar_mouse_input_tracker:subscribe(true)
end
end
function store_character_lost_focus()
if Hint_bar_mouse_input_tracker ~= -1 then
Hint_bar_mouse_input_tracker:subscribe(false)
end
end
function store_character_bg_anim_done()
store_common_bg_anim_complete()
end
--******************************* State transition functions *******************************************************
function store_character_lineup_to_gallery_download()
if push_screen("store_gallery") == true then
Store_common_gallery_download_only = true
Lineup_input_tracker:subscribe(false)
store_character_enable_mouse_split(false, false, false)
store_character_creation_add_clothes()
else
Store_character_current_state = LINEUP_STATE
end
end
function store_character_lineup_to_customization()
-- reset callbacks
Store_common_populate_list_cb = store_character_populate_list
Store_common_exit_cb = store_character_lineup_return_confirm
vint_apply_start_values(Store_character_in_anim.handle)
vint_apply_start_values(Player_creation_bg_anim.handle)
-- Get the index of the picture the player picked
local index = Susan_object:get_index()
local info = Store_character_lineup_presets[index]
-- switch input from lineup to customization
Lineup_input_tracker:subscribe(false)
store_character_enable_mouse_split(false, false, false)
-- load in our preset character
local preset_name = info.gender .. "_" .. info.race
local regional_preset_name = info.gender .. "_" .. info.figure
pcr_apply_preset(preset_name, "figure", regional_preset_name, false)
pcr_mark_changed()
-- We want the character to have clothes on at the top menu, and we need to change to preset clothing
pcr_add_default_clothing()
-- transition to customization interface
Store_character_anim_done = false
ui_audio_post_event("Police_Lineup")
local pcr_anim_in = Vdo_anim_object:new("player_creation_fade_in_anim")
local end_tween = Vdo_anim_object:new("susan_anchor_out_twn")
end_tween:set_property("end_event","store_character_lineup_susan_out_done")
vint_set_property(vint_object_find("player_creation_grp"), "background", true)
game_UI_audio_play("UI_Main_Menu_Select")
Active_list:set_visible(true)
Store_header:set_visible(true)
if game_get_platform() ~= "PC" then
Store_common_rotate_hint:set_visible(true)
end
vint_set_property(vint_object_find("police_logo"), "visible", true)
pcr_anim_in:play()
Store_character_in_anim:play(0)
Player_creation_bg_anim:play(0)
-- Change hint buttons
local hint_data = {
{CTRL_MENU_BUTTON_B, "MENU_BACK"},
}
Store_common_hint_bar:set_hints(hint_data)
character_enable_mouse_drag(true)
end
function store_character_gallery_download_to_lineup()
-- reset callbacks
Store_common_populate_list_cb = store_character_populate_list
Store_common_exit_cb = store_character_lineup_return_confirm
vint_apply_start_values(Store_character_in_anim.handle)
vint_apply_start_values(Player_creation_bg_anim.handle)
store_character_populate_list(Menu_data, Active_list:get_selection())
Active_list:set_visible(false)
Store_header:set_visible(false)
Store_common_rotate_hint:set_visible(false)
vint_set_property(vint_object_find("police_logo"), "visible", false)
Store_character_in_anim:play(0)
Player_creation_bg_anim:play(0)
local anim = Vdo_anim_object:new("player_creation_fade_out_anim")
anim:play()
Susan_object:play_intro()
vint_set_property(vint_object_find("player_creation_grp"), "background", false)
Store_character_anim_done = false
local end_tween = Vdo_anim_object:new("gradient_alpha_in_twn")
end_tween:set_property("end_event","store_character_lineup_susan_in_done")
-- start streaming images again
-- Get the index of the picture the player picked
local index = Susan_object:get_index()
-- Decrement the index to load the images before current highlight
index = index - 2
if index < 1 then
index = index + STORE_CHARACTER_NUM_LINEUP_IMAGES
end
image_streamer_start_streaming(index, STORE_CHARACTER_NUM_LINEUP_IMAGES_LOADED)
-- switch inputs back to lineup mode
Input_tracker:subscribe(false)
store_character_enable_mouse_split(false, false, false)
-- Change hint buttons
local hint_data = {
{CTRL_MENU_BUTTON_A, "CUST_CHOOSE_CHARACTER"},
}
Store_common_hint_bar:set_hints(hint_data)
Store_common_hint_bar:set_visible(true)
character_enable_mouse_drag(false)
Store_common_gallery_download_only = false
Store_header:clear_titles()
Store_header:push_title(nil, "STORE_CHOOSE_A_CATEGORY")
vint_set_property(vint_object_find("player_creation_grp", 0, Store_character_doc_handle), "background", false)
bg_saints_set_background(false)
end
function store_character_gallery_download_to_customization()
-- reset callbacks
Store_common_populate_list_cb = store_character_populate_list
Store_common_exit_cb = store_character_lineup_return_confirm
vint_apply_start_values(Store_character_in_anim.handle)
vint_set_property(vint_object_find("police_logo"), "visible", true)
local pcr_anim_in = Vdo_anim_object:new("player_creation_fade_in_anim")
Store_common_gallery_download_only = false
Store_header:set_visible(true)
Active_list:set_visible(true)
if game_get_platform() ~= "PC" then
Store_common_rotate_hint:set_visible(true)
end
Susan_object:set_visible(false)
pcr_anim_in:play(0)
Store_character_in_anim:play(0)
store_unlock_controls()
Store_header:clear_titles()
Store_header:push_title(nil, "STORE_CHOOSE_A_CATEGORY")
store_character_populate_list(Menu_data, Active_list:get_selection())
-- Cleans up images in the image streamer for lineup
store_character_lineup_susan_out_done()
end
function store_character_customization_to_lineup()
vint_apply_start_values(Store_character_in_anim.handle)
vint_apply_start_values(Player_creation_bg_anim.handle)
Active_list:set_visible(false)
Store_header:set_visible(false)
Store_common_rotate_hint:set_visible(false)
vint_set_property(vint_object_find("police_logo"), "visible", false)
Store_character_in_anim:play(0)
Player_creation_bg_anim:play(0)
local anim = Vdo_anim_object:new("player_creation_fade_out_anim")
anim:play()
Susan_object:play_intro()
vint_set_property(vint_object_find("player_creation_grp"), "background", false)
Store_character_anim_done = false
local end_tween = Vdo_anim_object:new("gradient_alpha_in_twn")
end_tween:set_property("end_event","store_character_lineup_susan_in_done")
-- start streaming images again
-- Get the index of the picture the player picked
local index = Susan_object:get_index()
-- Decrement the index to load the images before current highlight
index = index - 2
if index < 1 then
index = index + STORE_CHARACTER_NUM_LINEUP_IMAGES
end
image_streamer_start_streaming(index, STORE_CHARACTER_NUM_LINEUP_IMAGES_LOADED)
-- switch inputs back to lineup mode
Input_tracker:subscribe(false)
store_character_enable_mouse_split(false, false, false)
-- Change hint buttons
local hint_data = {
{CTRL_MENU_BUTTON_A, "CUST_CHOOSE_CHARACTER"},
}
Store_common_hint_bar:set_hints(hint_data)
character_enable_mouse_drag(false)
Susan_object:set_visible(true)
end
function store_character_customization_to_gallery_full()
if (push_screen("store_gallery") ~= true) then
Store_character_current_state = CUSTOMIZATION_STATE
end
end
function store_character_customization_to_finished()
end
function store_character_gallery_full_to_customization()
Store_common_gallery_download_only = false
pcr_strip_clothing()
store_unlock_controls()
-- reset our store stacks
store_common_stack_clear_menu()
store_common_stack_clear_id()
store_common_stack_clear_index()
-- Populate the areas and draw the first menu
store_character_main_menu_fill()
-- If we're in the character lineup, change the text on the "exit" choice, and what happens on back
local is_plastic_surgeon = pcr_is_plastic_surgeon()
if is_plastic_surgeon == false then
Menu_data[#Menu_data].label = "CUST_MENU_EXIT"
Menu_data[#Menu_data].is_new = true
Store_common_exit_cb = store_character_lineup_return_confirm
else
Store_common_exit_cb = store_character_exit
end
if game_get_platform() ~= "PC" then
Store_common_rotate_hint:set_visible(true)
end
-- Pass the table data to the megalist vdo.
store_character_populate_list(Menu_data, 1)
-- Set up some callbacks for the common store script
Store_common_populate_list_cb = store_character_populate_list
end
local Creation_state_transitions = {
[LINEUP_STATE] = {
[GALLERY_DOWNLOAD_STATE] = store_character_lineup_to_gallery_download,
[CUSTOMIZATION_STATE] = store_character_lineup_to_customization,
},
[GALLERY_DOWNLOAD_STATE] = {
[LINEUP_STATE] = store_character_gallery_download_to_lineup,
[CUSTOMIZATION_STATE] = store_character_gallery_download_to_customization,
},
[CUSTOMIZATION_STATE] = {
[LINEUP_STATE] = store_character_customization_to_lineup,
[GALLERY_FULL_STATE] = store_character_customization_to_gallery_full,
[FINISHED_STATE] = store_character_customization_to_finished,
},
[GALLERY_FULL_STATE] = {
[CUSTOMIZATION_STATE] = store_character_gallery_full_to_customization,
},
}
function store_character_change_state(new_state)
local state_change_func = Creation_state_transitions[Store_character_current_state][new_state]
-- don't do anything on an invalid state change
if state_change_func ~= nil then
Store_character_current_state = new_state
state_change_func()
end
end