The Wiki for Tale 8 is in read-only mode and is available for archival and reference purposes only. Please visit the current Tale 11 Wiki in the meantime.

If you have any issues with this Wiki, please post in #wiki-editing on Discord or contact Brad in-game.

Difference between revisions of "User:Augir/Necklace"

From A Wiki in the Desert
Jump to navigation Jump to search
(Undo revision 20476 by Augir (talk))
Line 22: Line 22:
  
 
Small Coral good color
 
Small Coral good color
 
  
 
Legend:
 
Legend:
Line 30: Line 29:
 
:X = Ruled out for that slot
 
:X = Ruled out for that slot
 
:C = Correct Color, Correct Slot
 
:C = Correct Color, Correct Slot
 
-- Vegetable Macro for Tale 7 by thejanitor.
 
--
 
-- Thanks to veggies.lua for the build button locations
 
-- Updated 29-SEP-2017 by Silden to take into account UI changes that meant the windows would not close properly
 
-- Updated 30-SEP-2017 by Silden to increase default values to cater for long veg names, such as Cabbage
 
 
dofile("common.inc")
 
dofile("settings.inc")
 
 
 
WARNING=[[
 
THIS IS A BETA MACRO YOU ARE USING AT YOUR OWN RISK
 
You must be in the fully zoomed in top down F8 F8 F8 view, Alt+L to lock the camere once there.
 
In User Options -> Interface Options -> Menu You must DISABLE: "Right-Click Pins/Unpins a Menu"
 
You Must ENABLE: "Right-Click opens a Menu as Pinned"
 
You Must ENABLE: "Use the chat area instead of popups for many messages"
 
In Options -> One-Click and Related -> You must DISABLE: "Plant all crops where you stand"
 
In Options -> Video -> You must set: Shadow Quality and Time of Day lighting to the lowest possible.
 
Do not move once the macro is running and you must be standing on a tile with water available to refill.
 
Do not stand directly on or within planting distance of actual animated water.
 
]]
 
 
DEBUG=false
 
 
-- How many times to search for a plant / try open a bed window before giving up.
 
SEARCH_RETRYS=10
 
 
-- How many pixels in a NxN grid to look for before
 
MATCH_GRID_SIZE=5
 
 
-- These are the times in seconds it waits before watering a plant for a given stage.
 
-- For example, plant A is planted at time 0. At time 2.8 seconds the macro queues up plant A to be watered, it then
 
-- sleeps 0.2 seconds until FIRST_STAGE_WAIT time has passed before watering that plant an moving on.
 
 
-- If you encounter problems where plants are dieing at various stages it is ethier because these values are too low
 
-- causing a plant to be watered 3+ times in a single stage before it grows. Or it is because they are too high and
 
-- a plant is not recieving its water in time before regressing a stage.
 
-- Finally if in the final harvest stage you get getting messages about running out of water then probably it is trying
 
-- to harvest before it is ready and trying to rewater a plants 3rd stage. Increase the harvest wait to hopefully fix this.
 
 
-- TODO: Scale these based on global (and ideally local) teppy time.
 
FIRST_STAGE_WAIT = 4
 
SECOND_STAGE_WAIT = 30
 
THIRD_STAGE_WAIT = 48
 
FORTH_STAGE_WAIT = 70
 
HARVEST_STAGE_WAIT = 83
 
 
STAGE_WAITS = { FIRST_STAGE_WAIT, SECOND_STAGE_WAIT, THIRD_STAGE_WAIT, FORTH_STAGE_WAIT, HARVEST_STAGE_WAIT }
 
 
-- How long to wait for the characters animations to stop at the end of each planting run. If this is too low
 
-- then instead of clicking a newly placed plant the macro will hit your character. So if you see at the start of a new
 
-- cycle the character menu being opened by the macro increase this value.
 
END_OF_RUN_WAIT = 3000
 
 
-- Controls the size of each search box. The larger this is the slower the search phase which can break everything.
 
SEARCH_BOX_SCALE = 1/10
 
 
MAX_PLANTS=12
 
 
RED = 0xFF2020ff
 
BLACK = 0x000000ff
 
WHITE = 0xFFFFFFff
 
 
LEEKS = "Horus' Grain"
 
SEED_NAMES = {
 
    "Tears of Sinai",
 
    "Green Leaf",
 
    "Bastet's Yielding",
 
    LEEKS,
 
    "Apep's Crop",
 
}
 
SEED_TYPES = {
 
    "Onions",
 
    "Carrots",
 
    "Cabbage",
 
    "Leeks",
 
    "Garlic",
 
}
 
 
-- Used to control the plant window placement and tiling.
 
WINDOW_HEIGHT=120 -- Was 80
 
WINDOW_WIDTH=285 -- Was 220
 
WINDOW_OFFSET_X=150
 
WINDOW_OFFSET_Y=150
 
 
function doit()
 
    while true do
 
        local config = makeReadOnly(getUserParams())
 
        askForWindowAndSetupGlobals(config)
 
        gatherVeggies(config)
 
    end
 
end
 
 
function askForWindowAndSetupGlobals(config)
 
    local min_jugs = config.num_waterings * config.num_plants * config.num_stages
 
    local min_seeds = config.num_plants + 8
 
    local one = 'You will need ' .. min_jugs .. ' jugs of water and at minimum ' .. min_seeds .. ' seeds \n'
 
    local two = '\n Press Shift over ATITD window to continue.'
 
    askForWindow(one .. two)
 
    setupGlobals()
 
end
 
 
function setupGlobals()
 
    NORTH  = Vector:new{0,-1}
 
    SOUTH  = Vector:new{0,1}
 
    WEST    = Vector:new{-1,0}
 
    EAST    = Vector:new{1,0}
 
    NORTH_WEST = NORTH + WEST
 
    NORTH_EAST = NORTH + EAST
 
    SOUTH_WEST = SOUTH + WEST
 
    SOUTH_EAST = SOUTH + EAST
 
    DOUBLE_SOUTH = SOUTH * 2
 
    DOUBLE_NORTH = NORTH * 2
 
    DOUBLE_WEST = WEST * 2
 
    DOUBLE_EAST = EAST * 2
 
 
    MOVE_BTNS = {}
 
    PLANT_LOCATIONS = {next=1}
 
    PlantLocation:new{direction_vector=NORTH, move_btn=Vector:new{59, 51}}
 
    PlantLocation:new{direction_vector=EAST ,move_btn=Vector:new{84, 74}}
 
    PlantLocation:new{direction_vector=SOUTH ,move_btn=Vector:new{60, 98}}
 
    PlantLocation:new{direction_vector=WEST ,move_btn=Vector:new{37, 75}}
 
    PlantLocation:new{direction_vector=NORTH, num_move_steps=2}
 
    PlantLocation:new{direction_vector=WEST, num_move_steps=2}
 
    PlantLocation:new{direction_vector=EAST, num_move_steps=2}
 
    PlantLocation:new{direction_vector=SOUTH, num_move_steps=2}
 
    PlantLocation:new{direction_vector=NORTH_EAST, move_btn=Vector:new{75, 62}}
 
    PlantLocation:new{direction_vector=NORTH_WEST, move_btn=Vector:new{45,60}}
 
    PlantLocation:new{direction_vector=SOUTH_EAST, move_btn=Vector:new{75, 91}}
 
    PlantLocation:new{direction_vector=SOUTH_WEST, move_btn=Vector:new{45, 87}}
 
    makeReadOnly(PLANT_LOCATIONS)
 
 
    local mid = getScreenMiddle()
 
    ANIMATION_BOX = makeBox(mid.x - 60, mid.y - 50, 105, 85)
 
    ARM_BOX = makeBox(mid.x - 90, mid.y - 20, 80, 25)
 
    BUILD_BTN = Vector:new{31, 135}
 
end
 
 
PlantLocation={}
 
function PlantLocation:new(o)
 
    if o.num_move_steps then
 
        o.move_btn = PLANT_LOCATIONS[o.direction_vector].move_btn
 
        o.direction_vector = o.direction_vector * o.num_move_steps
 
    else
 
        o.num_move_steps = 1
 
    end
 
    PLANT_LOCATIONS[o.direction_vector] = o
 
    PLANT_LOCATIONS[PLANT_LOCATIONS.next] = o
 
    PLANT_LOCATIONS.next = PLANT_LOCATIONS.next + 1
 
    o.box = makeSearchBox(o.direction_vector)
 
    return newObject(PlantLocation, o, true)
 
end
 
 
function PlantLocation:move()
 
    for step=1,self.num_move_steps do
 
        click(self.move_btn)
 
    end
 
end
 
 
function gatherVeggies(config)
 
    local plants = Plants:new{num_plants=config.num_plants }
 
 
    drawWater()
 
    for _=1,config.num_runs do
 
        local start = lsGetTimer()
 
 
        checkBreak()
 
        --lsSleep(3000)
 
 
        plants:iterate(Plant.plant, config)
 
        for round=1,config.num_stages+1 do
 
            plants:iterate(Plant.water, {stage_wait=STAGE_WAITS[round], num_waterings=config.num_waterings})
 
            checkBreak()
 
        end
 
 
        plants:iterate(Plant.close, config)
 
 
        lsSleep(click_delay)
 
        drawWater()
 
        lsSleep(click_delay*5)
 
        checkBreak()
 
 
 
        local stop = lsGetTimer() + END_OF_RUN_WAIT
 
        local total = math.floor((3600 / ((stop - start)/1000)) * config.num_plants * 9) -- default 3, currently 9 veggie yield with pyramids bonus
 
        sleepWithStatus(END_OF_RUN_WAIT, "Running at " .. total .. " veg per hour. ")
 
    end
 
end
 
 
-- Simple container object which constructs N plants and allows iteration over them.
 
Plants={}
 
function Plants:new(o)
 
    for index=1,o.num_plants do
 
        local location = PLANT_LOCATIONS[index]
 
        self[index] = Plant:new{index=index, location=location}
 
    end
 
    return newObject(self,o,true)
 
end
 
 
function Plants:iterate(func, args)
 
    for index=1,self.num_plants do
 
        func(self[index], args)
 
    end
 
end
 
 
Plant = {}
 
function Plant:new(o)
 
    o.window_pos = indexToWindowPos(o.index)
 
    return newObject(self,o)
 
end
 
 
function Plant:plant(config)
 
    -- Take of a snapshot of the area in which we are guessing the plant will be placed before we actually create
 
    -- and place it.
 
    if not self.saved_plant_location then
 
        lsSleep(click_delay)
 
        self.beforePlantPixels = getBoxPixels(self.location.box)
 
    end
 
 
    clickPlantButton(config.seed_name)
 
    self.location:move()
 
    local spot = getWaitSpotAt(BUILD_BTN)
 
    click(BUILD_BTN)
 
    self.plant_time = lsGetTimer()
 
    waitForChange(spot, click_delay*5)
 
    lsSleep(click_delay)
 
 
    if not self.saved_plant_location then
 
        for _=1,SEARCH_RETRYS do
 
            if self:searchForPlant() then
 
                break
 
            end
 
            lsPrintln("Search retry for plant " .. self.index)
 
            lsSleep(tick_delay)
 
        end
 
        if not self.saved_plant_location then
 
            lsPrintln("Fail search for plant" .. self.index)
 
        end
 
 
    end
 
 
    self:openBedWindow(config.alternate_drag)
 
end
 
 
function Plant:searchForPlant()
 
    lsPrintln("Searching for plant " .. self.index)
 
    return findChangedRow(self.location.box, self.beforePlantPixels,
 
        function (location, pixels)
 
            self.saved_plant_location = location
 
            self.saved_plant_pixels = pixels
 
        end
 
    )
 
end
 
 
function Plant:openBedWindow(alternate_drag)
 
    if not self.saved_plant_location then
 
        lsPrintln("No Saved location for plant " .. self.index)
 
        return
 
    end
 
 
    -- Wierd hacky thing, move the mouse to where the window will be and then safeClick the plant which causes
 
    -- the window to open instantly at the desired location and not where we clicked the plant.
 
    -- TODO: problably do something different as this is the only thing that takes mouse control from the user.
 
 
    self.window_open = false
 
    local window_spot = getWaitSpotAt(self.window_pos + {5,5})
 
    for _=1, SEARCH_RETRYS do
 
        if waitForPixelsAt(self.saved_plant_location, self.location.box, self.saved_plant_pixels) then
 
            if alternate_drag then
 
                local open_spot = getWaitSpotAt(self.saved_plant_location + {5,5})
 
                click(self.saved_plant_location,true,true)
 
                if waitForChange(open_spot,click_delay*5) then
 
                    lsSleep(click_delay)
 
                    drag(self.saved_plant_location.x+5,self.saved_plant_location.y+5,self.window_pos.x,self.window_pos.y,click_delay*2)
 
                end
 
            else
 
                moveMouse(self.window_pos)
 
                click(self.saved_plant_location ,1)
 
            end
 
 
            self.window_open = waitForChange(window_spot, click_delay*5)
 
            if self.window_open then
 
                break
 
            end
 
        end
 
        lsPrintln("Bed window open retry for plant " .. self.index)
 
        lsSleep(click_delay)
 
    end
 
 
    if not self.window_open then
 
        lsPrintln("Bed window open fail for plant " .. self.index)
 
    end
 
end
 
 
function waitForPixelsAt(vector, box, pixels)
 
    local match_size = MATCH_GRID_SIZE
 
    local half_match_size = math.floor(match_size/2)
 
    local box_location_x = vector.x - half_match_size
 
    local box_location_y = vector.y - half_match_size
 
    local match_box = makeBox(box_location_x, box_location_y, match_size, match_size)
 
    local all_same = true
 
    iterateBoxPixels(match_box, function(x,y,pixel)
 
        local pixels_x = box_location_x + x - box.left
 
        local pixels_y = box_location_y + y - box.top
 
        local old_pixel = pixels[pixels_y][pixels_x]
 
        local diff = calculatePixelDiffs(old_pixel, pixel)
 
        local diff_ok = diff[1] < 10 and diff[2] < 10 and diff[3] < 10
 
        all_same = all_same and diff_ok
 
    end)
 
    return all_same
 
end
 
 
 
-- For a given plants index sleep until time_seconds has passed for that plant since it was planted.
 
function Plant:sleepUntil(time_seconds)
 
    local sleepTime = time_seconds*1000 - (lsGetTimer() - self.plant_time);
 
    if sleepTime > 0 then
 
        sleepWithStatus(sleepTime, "Sleeping for " .. sleepTime)
 
    end
 
end
 
 
function Plant:clickWindow(offset,right_click,show)
 
    if self.window_open then
 
        click(self.window_pos + offset,right_click,show)
 
    end
 
end
 
 
function Plant:water(args)
 
    if not self.window_open then
 
        lsPrintln("Trying to water plant " .. self.index .. " which has no window open")
 
        return
 
    end
 
 
    checkBreak()
 
    self:sleepUntil(args.stage_wait)
 
 
    local search_box = makeBox(self.window_pos.x,self.window_pos.y-40,40,40)
 
    srReadScreen()
 
    local this_loc = srFindImageInRange("This.png", search_box.left, search_box.top, search_box.width, search_box.height, 4800)
 
    if not this_loc then
 
        lsPrintln("Didn't find This text for plant " .. self.index)
 
        self.window_open = false
 
        return
 
    end
 
    this_loc = Vector:new{x=this_loc[0],y=this_loc[1]+10}
 
 
    click(this_loc)
 
    for _=1, args.num_waterings do
 
        lsSleep(click_delay)
 
        click(this_loc+{0,25})
 
        click(this_loc)
 
        checkBreak()
 
    end
 
end
 
 
function Plant:close(config)
 
    -- New UI changes affected the closing of finished windows.
 
    -- Now look for the UnPin image, and safeClick it closed if found.
 
   
 
    local search_box = makeBox(self.window_pos.x+65,self.window_pos.y-50,220,80)
 
    srReadScreen()
 
    local unpin_loc = srFindImageInRange("UnPin.png", search_box.left, search_box.top, search_box.width, search_box.height, 4800)
 
    if unpin_loc then
 
safeClick(unpin_loc[0],unpin_loc[1]);
 
    else
 
lsPrintln("Didn't find unpin image for plant " .. self.index .. " looking left:" .. search_box.left .. ", top: " .. search_box.top .. ", width: " .. search_box.width .. ", height: " .. search_box.height .. ".");
 
        return
 
    end
 
end
 
 
-- Create a table of direction string -> box. Each box is where we will search the plant placed for that given direction
 
-- string.
 
-- Full of janky hardcoded values.
 
-- TODO: Make debuging this easier, figure out pixel scaling for different resolutions, get rid of magic numbers.
 
function makeSearchBox(direction)
 
    local xyWindowSize = srGetWindowSize()
 
    local search_size = math.floor(xyWindowSize[0] * SEARCH_BOX_SCALE)
 
    local mid = getScreenMiddle()
 
    local offset_mid = mid - {math.floor(search_size / 3), math.floor(search_size / 3) }
 
 
    local top_left = offset_mid + direction*40 - Vector:new{20,20 }
 
 
    local box = makeBox(top_left.x,top_left.y, search_size, search_size)
 
    box.direction = direction
 
    return box
 
end
 
 
function getScreenMiddle()
 
    local xyWindowSize = srGetWindowSize()
 
    return Vector:new{math.floor(xyWindowSize[0]/2), math.floor(xyWindowSize[1]/2)}
 
end
 
 
-- Tiling method from Cinganjehoi's original bash script. Tried out the automato common ones but they are slow
 
-- and broke sometimes? This is super simple and its not the end of the world if it breaks a little during a run.
 
function indexToWindowPos(index)
 
    local columns = getNumberWindowColumns()
 
    local x = WINDOW_WIDTH*((index-1) % columns) + WINDOW_OFFSET_X
 
    local y = WINDOW_HEIGHT*math.floor((index-1) / columns) + WINDOW_OFFSET_Y
 
    return Vector:new{x, y}
 
end
 
 
function getNumberWindowColumns()
 
    local xyWindowSize = srGetWindowSize()
 
    local width = xyWindowSize[0] * 0.6
 
    return math.floor(width / WINDOW_WIDTH);
 
end
 
 
function clickPlantButton(seed_name)
 
    local build_menu_opened = false
 
    while not build_menu_opened do
 
        local plantButton = findText(seed_name)
 
        if plantButton then
 
            local spot = getWaitSpotAt(Vector:new{5,5})
 
            clickText(plantButton, 1)
 
            build_menu_opened = waitForChange(spot,click_delay*5)
 
        else
 
    lsPlaySound("fail.wav");
 
            error("Text " .. seed_name .. " Not found.")
 
        end
 
        sleepWithStatus(tick_delay, "Planting...") --Retrying build menu open
 
    end
 
end
 
 
function getBoxPixels(box)
 
    local pixels = {}
 
    iterateBoxPixels(box,
 
        function(x,y,pixel)
 
            pixels[y][x] = pixel
 
        end,
 
        function(y)
 
            pixels[y] = {}
 
        end
 
    )
 
    return pixels
 
end
 
 
-- Finds a row of pixels which have changed in the current ReadScreen buffer compared to a given 2d array of pixels.
 
function findChangedRow(box, pixels, func)
 
    if DEBUG then
 
        debugShowBox(box)
 
    end
 
 
    local changed = {}
 
    local new_pixels = {}
 
    iterateBoxPixels(box,
 
        function(x,y,pixel)
 
            changed[y][x] = pixels[y][x] ~= pixel
 
            new_pixels[y][x] = pixel
 
        end,
 
        function(y)
 
            changed[y] = {}
 
            new_pixels[y] = {}
 
        end
 
    )
 
 
    local search_from_left = box.direction.x <= 0
 
    local search_from_top = box.direction.y < 0
 
 
    local y_start, y_end, y_inc = 0, math.floor(box.height/MATCH_GRID_SIZE)-1, 1
 
    if not search_from_top then
 
        y_start, y_end, y_inc = y_end, y_start, -1
 
    end
 
    local x_start, x_end, x_inc = 0, math.floor(box.width/MATCH_GRID_SIZE)-1, 1
 
    if not search_from_left then
 
        x_start, x_end, x_inc = x_end, x_start, -1
 
    end
 
 
    for y=y_start,y_end,y_inc do
 
        for x=x_start,x_end,x_inc do
 
            local all_changed = true
 
            for j=0,MATCH_GRID_SIZE-1 do
 
                for k=0,MATCH_GRID_SIZE-1 do
 
                    local changed_x = x*MATCH_GRID_SIZE+k
 
                    local changed_y = y*MATCH_GRID_SIZE+j
 
                    all_changed = all_changed and changed[changed_y][changed_x]
 
                end
 
            end
 
            if all_changed then
 
                local grid_centre_x = x*MATCH_GRID_SIZE + math.floor(MATCH_GRID_SIZE/2)
 
                local grid_centre_y = y*MATCH_GRID_SIZE + math.floor(MATCH_GRID_SIZE/2)
 
                func(Vector:new{box.left + grid_centre_x,box.top + grid_centre_y},new_pixels)
 
                return true
 
            end
 
        end
 
    end
 
    return false
 
end
 
 
function debugShowBox(box)
 
    srSetMousePos(box.left, box.top)
 
    sleepWithStatus(2000, "TOP LEFT")
 
    srSetMousePos(box.right, box.bottom)
 
    sleepWithStatus(2000, "BOT RIGHT")
 
end
 
 
function inside(vector,box)
 
    local x, y = vector.x, vector.y
 
    return (x >= box.left and x <= box.right) and (y >= box.top and y <= box.bottom)
 
end
 
 
function distanceCentre(vector)
 
    local mid = getScreenMiddle()
 
    local delta = vector - mid
 
 
    local dx = math.pow(delta.x,2)
 
    local dy = math.pow(delta.y,2)
 
 
    return math.sqrt(dx + dy)
 
end
 
 
function iterateBoxPixels(box, xy_func, y_func)
 
    srReadScreen()
 
 
 
    for y=0,box.height,1 do
 
        if y_func then y_func(y) end
 
        for x=0, box.width do
 
            local pixel = srReadPixelFromBuffer(box.left + x, box.top + y)
 
            if xy_func(x,y,pixel) then
 
                return
 
            end
 
        end
 
        checkBreak()
 
    end
 
end
 
 
 
-- Used to place gui elements sucessively.
 
current_y = 0
 
-- How far off the left hand side to place gui elements.
 
X_PADDING = 5
 
 
function getUserParams()
 
    local is_done = false
 
    local got_user_params = false
 
    local use_custom = readSetting("use_custom",false)
 
    local config = {alternate_drag=readSetting("alternate_drag")}
 
    config.seed_name = readSetting("seed_name","")
 
    config.num_waterings = readSetting("num_waterings",1)
 
    config.num_stages = readSetting("num_stages",1)
 
    local seed_index = readSetting("seed_index",1)
 
    local display_seed_names = {}
 
    for i=1,5 do
 
        display_seed_names[i] = SEED_NAMES[i] .. " (" .. SEED_TYPES[i] .. ")"
 
    end
 
    while not is_done do
 
        current_y = 10
 
 
        if not got_user_params then
 
            local max_plants      = MAX_PLANTS
 
            use_custom = lsCheckBox(X_PADDING, current_y, 10, WHITE, "Use custom seed?", use_custom)
 
            current_y = 45
 
            if use_custom then
 
                config.seed_name    = drawEditBox("seed_name", "Custom seed name? ", config.seed_name, false)
 
                config.num_waterings = drawNumberEditBox("num_waterings", "How many waterings per stage? ", config.num_waterings)
 
                config.num_stages    = drawNumberEditBox("num_stages", "How many watering stages? ", config.num_stages)
 
            else
 
                seed_index        = lsDropdown("seed_name", X_PADDING, current_y, 10, lsScreenX - 10, seed_index, display_seed_names)
 
                current_y = 85
 
            end
 
            config.num_plants      = drawNumberEditBox("num_plants", "How many to plant per run? Max " .. max_plants, 12)
 
            config.num_runs        = drawNumberEditBox("num_runs", "How many runs? ", 20)
 
            config.click_delay    = drawNumberEditBox("click_delay", "What should the click delay be? ", 100)
 
            config.alternate_drag  = lsCheckBox(X_PADDING, current_y, 10, WHITE, "Alternate (slow) dragging?", config.alternate_drag)
 
            got_user_params = true
 
            for k,v in pairs(config) do
 
                got_user_params = got_user_params and v
 
            end
 
            got_user_params = got_user_params and drawBottomButton(lsScreenX - 5, "Next step")
 
        else
 
            drawWrappedText(WARNING, RED, X_PADDING, current_y)
 
 
            is_done = drawBottomButton(lsScreenX - 5, "Start Script")
 
        end
 
 
        if drawBottomButton(110, "Exit Script") then
 
            error "Script exited by user"
 
        end
 
 
        lsDoFrame()
 
        lsSleep(10)
 
    end
 
 
    writeSetting("seed_index",seed_index)
 
    writeSetting("alternate_drag",config.alternate_drag)
 
    writeSetting("use_custom",use_custom)
 
    writeSetting("seed_name",config.seed_name)
 
    writeSetting("num_waterings",config.num_waterings)
 
    writeSetting("num_stages",config.num_stages)
 
    config.num_plants = limitMaxPlants(config.num_plants)
 
    if not use_custom then
 
        config.seed_name = SEED_NAMES[seed_index]
 
        config.num_waterings = config.seed_name == LEEKS and 3 or 2
 
        config.num_stages = 3
 
    end
 
    click_delay = config.click_delay
 
    return config
 
end
 
 
function limitMaxPlants(user_supplied_max_num)
 
    return math.min(12, user_supplied_max_num)
 
end
 
 
function drawNumberEditBox(key, text, default)
 
    return drawEditBox(key, text, default, true)
 
end
 
 
function drawEditBox(key, text, default, validateNumber)
 
    drawTextUsingCurrent(text, WHITE)
 
    local width = validateNumber and 50 or 200
 
    local height = 30
 
    local done, result = lsEditBox(key, X_PADDING, current_y, 0, width, height, 1.0, 1.0, BLACK, default)
 
    if validateNumber then
 
        result = tonumber(result)
 
    elseif result == "" then
 
        result = false
 
    end
 
    if not result then
 
        local error = validateNumber and "Please enter a valid number!" or "Enter text!"
 
        drawText(error, RED, X_PADDING + width + 5, current_y + 5)
 
        result = false
 
    end
 
    current_y = current_y + 35
 
    return result
 
end
 
 
function drawTextUsingCurrent(text, colour)
 
    drawText(text, colour, X_PADDING, current_y)
 
    current_y = current_y + 20
 
end
 
function drawText(text, colour, x, y)
 
    lsPrint(x, y, 10, 0.7, 0.7, colour, text)
 
end
 
 
function drawWrappedText(text, colour, x, y)
 
    lsPrintWrapped(x, y, 10, lsScreenX-10, 0.6, 0.6, colour, text)
 
end
 
 
function drawBottomButton(xOffset, text)
 
    return lsButtonText(lsScreenX - xOffset, lsScreenY - 30, z, 100, WHITE, text)
 
end
 
 
-- Simple immutable vector class
 
Vector={}
 
function Vector:new(o)
 
    o.x = o.x or o[1]
 
    o.y = o.y or o[2]
 
    return newObject(self, o, true)
 
end
 
 
function Vector:__add(vector)
 
    local x,y = Vector.getXY(vector)
 
    return Vector:new{self.x + x, self.y + y}
 
end
 
 
function Vector:__sub(vector)
 
    local x,y = Vector.getXY(vector)
 
    return Vector:new{self.x - x, self.y - y}
 
end
 
 
function Vector:__div(divisor)
 
    return Vector:new{self.x / divisor, self.y / divisor}
 
end
 
 
function Vector:__mul(multiplicand)
 
    return Vector:new{self.x * multiplicand, self.y * multiplicand}
 
end
 
 
function Vector.getXY(vector)
 
    return vector.x or vector[1], vector.y or vector[2]
 
end
 
 
function Vector:length()
 
    return math.sqrt(self.x^2 + self.y^2)
 
end
 
 
function Vector:normalize()
 
    return self / self:length()
 
end
 
 
function Vector:__tostring()
 
    return "(" .. self.x .. ", " .. self.y .. ")"
 
end
 
 
 
 
function click(vector, right_click, show_mouse)
 
    if show_mouse then
 
        srClickMouse(vector.x, vector.y, right_click)
 
    else
 
        safeClick(vector.x, vector.y, right_click)
 
    end
 
    lsSleep(click_delay)
 
end
 
 
function moveMouse(vector)
 
    srSetMousePos(vector.x, vector.y)
 
    lsSleep(click_delay)
 
end
 
 
function getWaitSpotAt(vector)
 
    return getWaitSpot(vector.x, vector.y)
 
end
 
 
-- Helper function used in an objects constructor to setup its metatable correctly allowing for basic inheritence.
 
function newObject(class, o, read_only)
 
    o = o or {}
 
    setmetatable(o, class)
 
    class.__index = class
 
    if read_only then
 
        makeReadOnly(o)
 
    end
 
    return o
 
end
 
 
function makeReadOnly(table)
 
    local mt = getmetatable(table)
 
    if not mt then
 
        mt = {}
 
        if not table then print(debug.traceback()) end
 
        setmetatable(table,mt)
 
    end
 
    mt.__newindex = function(t,k,v)
 
        error("Attempt to update a read-only table", 2)
 
    end
 
    return table
 
end
 

Revision as of 04:01, 25 April 2018

Necklace - COMPLETED!!!!

Size/Color Small Medium Large Huge
1 2 3 4 5 6 7 1 2 3 4 1 2 1
Aqua - - - - - - C - - - - - -
Beige - - - - - - - - - - - - - -
Black - - - - - - - - - - - - - -
Coral C x C x - - - - - - - - - -
Pink - - - - - - - - - - - x x -
Smoke - - - - - - - - - - - - - -
White - - - - - - - - - - - - - -

Small Coral good color

Legend:

- = Unknown
'#' = Currently in slot
X = Ruled out for that slot
C = Correct Color, Correct Slot