USING "globals.sch" USING "commands_clock.sch" FUNC BOOL ARE_ANY_EVENTS_PENDING_IN_ORGANISER() IF g_iOrganiserEventsCurrentlyLogged > 0 RETURN TRUE ENDIF RETURN FALSE ENDFUNC FUNC STRING GET_ORGANISER_CATEGORY_STRING(ORGANISER_EVENT_ENUM paramEvent) // Add switch cases with text file entries describing each organiser event. SWITCH paramEvent CASE ORG_EVT_NOTHING RETURN "ORG_EV_0" BREAK CASE ORG_EVT_HOUSE_VIEWING RETURN "ORG_EV_1" BREAK ENDSWITCH RETURN "ORG_EV_0" ENDFUNC /// PURPOSE: /// Intialises or resets the caret of the organiser system PROC INTIALISE_ORGANISER() g_iFocusHour = 0 g_iFocusDay = 0 g_iOrgCurrentHour = GET_CLOCK_HOURS() g_iOrgCurrentDay = GET_CLOCK_DAY_OF_MONTH() g_iOrgCurrentMonth = ENUM_TO_INT(GET_CLOCK_MONTH()) ENDPROC /// PURPOSE: /// Empties the organiser log and resets the date PROC PURGE_AND_RESET_LOG() g_iFocusHour = 0 g_iFocusDay = 0 //ORGANISER_EVENT_STRUCT g_OrganiserUpcomingWeek[7][24] INT w = 0 INT d = 0 REPEAT 7 w REPEAT 24 d g_OrganiserUpcomingWeek[w][d].type = ORG_EVT_NOTHING ENDREPEAT ENDREPEAT INTIALISE_ORGANISER() ENDPROC /// PURPOSE: /// Get the number of days in the current month /// PARAMS: /// month - the month to find the index of /// RETURNS: /// the days in the given month FUNC INT GET_DAYS_IN_MONTH_ENUM(MONTH_OF_YEAR month)//INCOMPLETE //30 days hath September, //April, June, and November. //All the rest have 31 //but February’s the shortest one. //With 28 days most of the time, //until Leap Year gives us 29 (but fuck that shit) SWITCH month CASE SEPTEMBER RETURN 30 CASE APRIL RETURN 30 CASE JUNE RETURN 30 CASE NOVEMBER RETURN 30 CASE FEBRUARY //lol leap year? RETURN 28 DEFAULT RETURN 31 ENDSWITCH RETURN 0 ENDFUNC FUNC INT GET_DAYS_IN_MONTH_INT(INT month) RETURN GET_DAYS_IN_MONTH_ENUM(INT_TO_ENUM(MONTH_OF_YEAR,month)) ENDFUNC /// PURPOSE: /// Gap cannot be greater than 11 months, needs testing /// PARAMS: /// dayOne - start day /// monthOne - start month /// dayTwo - end day /// monthTwo - end month /// RETURNS: /// Time in days between the given months FUNC INT GET_GAP_IN_DAYS_BETWEEN_DAYS_MONTHS(INT dayOne,INT monthOne,INT dayTwo,INT monthTwo) INT i = 0//iterator //if this does not occure in the same month IF NOT (monthOne = monthTwo) //if the months are in the same year IF monthOne < monthTwo //if its not in the same month //get days to end of start month INT iDayToEnd = GET_DAYS_IN_MONTH_INT(monthOne)-(GET_DAYS_IN_MONTH_INT(monthOne)- dayOne) //get gap between months, sum up the days INT iDaysBetweenMonths = 0 //if the gap is not 0 IF monthTwo > (monthOne+1) //find the day total of the intervening months INT iGapRepeat = monthTwo - monthOne INT iFocusMonth = monthOne+1 i = 0 REPEAT iGapRepeat i IF iFocusMonth > 11 iFocusMonth = 0 ENDIF iDaysBetweenMonths += GET_DAYS_IN_MONTH_INT(iFocusMonth) ++iFocusMonth ENDREPEAT ENDIF //get days from start of end month INT iDayFromStart = GET_DAYS_IN_MONTH_INT(monthTwo) - dayTwo //sum INT hourgap = iDayToEnd + iDaysBetweenMonths + iDayFromStart PRINTSTRING("GET_GAP_IN_DAYS_BETWEEN_DAYS_MONTHS: day gap is ") PRINTINT(hourgap) PRINTNL() RETURN hourgap ENDIF ELSE //if it is in the same month //check for nonsense day value IF dayTwo < dayOne SCRIPT_ASSERT("GET_GAP_IN_DAYS_BETWEEN_DAYS_MONTHS: same month and day one is smaller than day 1") RETURN 0 ENDIF IF dayTwo = dayOne //zero day difference RETURN 0 ENDIF PRINTSTRING("GET_GAP_IN_DAYS_BETWEEN_DAYS_MONTHS: day gap is") PRINTINT(dayTwo - dayOne) PRINTNL() RETURN (dayTwo - dayOne) ENDIF SCRIPT_ASSERT("GET_GAP_IN_DAYS_BETWEEN_DAYS_MONTHS: script execution path fell through") RETURN 0 ENDFUNC /// PURPOSE: /// Updates the organiser system to cope with changing time /// TODO: register alert when coming into range of a new event //2 hour alert and //15 minute alert PROC CHECK_FOR_ORGANISER_TIME_CHANGE() IF NOT g_bFirstOrganiserUpdateDone PRINTSTRING("CHECK_FOR_ORGANISER_TIME_CHANGE: first time update intialisation") PRINTNL() g_iOrgCurrentMonth = ENUM_TO_INT(GET_CLOCK_MONTH()) g_iOrgCurrentDay = GET_CLOCK_DAY_OF_MONTH() g_iOrgCurrentHour = GET_CLOCK_HOURS() g_bFirstOrganiserUpdateDone = TRUE g_iFocusDay = ENUM_TO_INT(GET_CLOCK_DAY_OF_WEEK()) g_iFocusHour = GET_CLOCK_HOURS() PURGE_AND_RESET_LOG() EXIT ENDIF //check for month changing INT newmonth = ENUM_TO_INT(GET_CLOCK_MONTH()) INT newday = GET_CLOCK_DAY_OF_MONTH() INT newhour = GET_CLOCK_HOURS() INT oldmonth = g_iOrgCurrentMonth INT oldday = g_iOrgCurrentDay INT oldhour = g_iOrgCurrentHour //INT ShiftedOnByHours = 0 INT iDayCaret = 0 INT iHourCaret = 0 INT i = 0 INT j = 0 INT iHourDiff = 0 //TODO, check for year change INT iDaysBetweenDayMonth = GET_GAP_IN_DAYS_BETWEEN_DAYS_MONTHS(oldday,oldmonth,newday,newmonth) //is it not zero IF iDaysBetweenDayMonth > 0 iHourDiff = 0 IF iDaysBetweenDayMonth > 7 //obliterate week schedule i = 0 j = 0 REPEAT 7 i REPEAT 24 j IF NOT (g_OrganiserUpcomingWeek[iDayCaret][iHourCaret].type = ORG_EVT_NOTHING) g_iOrganiserChangeValue++ g_iOrganiserEventsCurrentlyLogged-- ENDIF g_OrganiserUpcomingWeek[iDayCaret][iHourCaret].type = ORG_EVT_NOTHING ENDREPEAT ENDREPEAT ELSE //day change is less than 7 //add hours from days between old and new to hour gap IF iDaysBetweenDayMonth > 1 iHourDiff += (iDaysBetweenDayMonth-1)*24 ENDIF ENDIF //apply hours to end of day from oldhour iHourDiff += (24 - oldhour) //apply hours from start of day from newhour iHourDiff += 24 - (24 - newhour) //figure out where //INT g_iFocusHour //INT g_iFocusDay //should be set i = 0 iHourCaret = g_iFocusHour iDayCaret = g_iFocusDay REPEAT iHourDiff i IF iHourCaret > 23 iHourCaret = 0 iDayCaret++ ENDIF IF iDayCaret > 6 iDayCaret = 0 ENDIF IF NOT (g_OrganiserUpcomingWeek[iDayCaret][iHourCaret].type = ORG_EVT_NOTHING) g_iOrganiserChangeValue++ g_iOrganiserEventsCurrentlyLogged-- ENDIF g_OrganiserUpcomingWeek[iDayCaret][iHourCaret].type = ORG_EVT_NOTHING iHourCaret++ ENDREPEAT //update global caret now g_iFocusHour = iHourCaret-1 g_iFocusDay = iDayCaret ELSE //check for hour difference IF oldhour > newhour //something crazy going on, assert SCRIPT_ASSERT("CHECK_FOR_ORGANISER_TIME_CHANGE: old hour is greater than new within the same day, wtf time travel?") PRINTSTRING("CHECK_FOR_ORGANISER_TIME_CHANGE: old hour is greater than new within the same day, wtf time travel?") PRINTNL() //panic and reset the time g_iOrgCurrentMonth = newmonth g_iOrgCurrentDay = newday g_iOrgCurrentHour = newhour g_iFocusDay = ENUM_TO_INT(GET_CLOCK_DAY_OF_WEEK()) g_iFocusHour = GET_CLOCK_HOURS() EXIT ENDIF IF oldhour = newhour //no change //PRINTSTRING("CHECK_FOR_ORGANISER_TIME_CHANGE: No hour change in update") //PRINTNL() EXIT ELSE //hour has changed within one day //find the hour difference iHourDiff = (newhour - oldhour) //update carat and clear out events i = 0 iHourCaret = g_iFocusHour iDayCaret = g_iFocusDay REPEAT iHourDiff i IF NOT (g_OrganiserUpcomingWeek[iDayCaret][iHourCaret].type = ORG_EVT_NOTHING) g_iOrganiserChangeValue++ g_iOrganiserEventsCurrentlyLogged-- ENDIF g_OrganiserUpcomingWeek[iDayCaret][iHourCaret].type = ORG_EVT_NOTHING iHourCaret++ IF iHourCaret > 23 iHourCaret = 0 iDayCaret++ ENDIF IF iDayCaret > 6 iDayCaret = 0 ENDIF ENDREPEAT //update global caret now g_iFocusHour = iHourCaret g_iFocusDay = iDayCaret ENDIF ENDIF //print out the update details PRINTSTRING("CHECK_FOR_ORGANISER_TIME_CHANGE: Organiser step updated") PRINTNL() IF g_iOrganiserChangeValue > g_iOrganiserChangeValueLastUpdate PRINTSTRING("Number of organiser events obliterated this update : ") PRINTINT(g_iOrganiserChangeValue-g_iOrganiserChangeValueLastUpdate) PRINTNL() ENDIF IF (oldmonth = newmonth) AND (oldhour = newhour) AND (oldday = newday) //PRINTSTRING("CHECK_FOR_ORGANISER_TIME_CHANGE: Time not changed!") //PRINTNL() ELSE PRINTSTRING("CHECK_FOR_ORGANISER_TIME_CHANGE: Time changed by ") PRINTINT(iHourDiff) PRINTNL() //print start and end times //update current times g_iOrgCurrentMonth = newmonth g_iOrgCurrentDay = newday g_iOrgCurrentHour = newhour ENDIF ENDPROC /// PURPOSE: /// Applies a selected event to a given time in the organiser timeslot relative to the current time /// PARAMS: /// event_type - the type of event to apply /// hours_from_now - the number of hours from the current time to add the event at /// days_from_now - the number of days from the current time to add the event at /// duration_in_hours - How long the event should be /// overwrite_other_events - TRUE: Causes this event to overlap others FALSE:only overwrites empty slots PROC PLACE_EVENT_IN_ORGANISER_TIMESLOT(ORGANISER_EVENT_ENUM event_type,INT hours_from_now,INT days_from_now,INT duration_in_hours,BOOL overwrite_other_events) CHECK_FOR_ORGANISER_TIME_CHANGE() //TODO error checking on values g_iOrganiserChangeValue++ //total hours in future INT hours_in_future = (days_from_now*24) + hours_from_now IF (hours_in_future+duration_in_hours) > (7*24) //event would overlap end of log, fail PRINTSTRING("PLACE_EVENT_IN_ORGANISER_TIMESLOT: Event overlaps end of log range, fail") PRINTNL() EXIT ENDIF INT i = 0 INT iDayIndex = g_iFocusDay INT iHourIndex = g_iFocusHour //Shift day and hour index onwards by hours_in_future REPEAT hours_in_future i IF iHourIndex > 23 iHourIndex = 0 ++iDayIndex ENDIF IF iDayIndex > 6 iDayIndex = 0 ENDIF ++iHourIndex ENDREPEAT //INT iDayDebugStart = iDayIndex //INT iHourDebugStart = iHourIndex i = 0 //apply the event for duration REPEAT duration_in_hours i IF iHourIndex > 23 iHourIndex = 0 ++iDayIndex ENDIF IF iDayIndex > 6 iDayIndex = 0 ENDIF BOOL doWrite = TRUE IF NOT overwrite_other_events //only overwrite empty IF NOT (g_OrganiserUpcomingWeek[iDayIndex][iHourIndex].type = ORG_EVT_NOTHING) doWrite = FALSE ENDIF ENDIF IF doWrite g_OrganiserUpcomingWeek[iDayIndex][iHourIndex].type = event_type g_iOrganiserEventsCurrentlyLogged++ ENDIF ++iHourIndex ENDREPEAT PRINTSTRING("PLACE_EVENT_IN_ORGANISER_TIMESLOT: Event written to log") PRINTNL() ENDPROC PROC PLACE_EVENT_IN_ABSOLUTE_ORGANISER_TIMESLOT(ORGANISER_EVENT_ENUM event_type, INT hourTarget, DAY_OF_WEEK dayTarget,INT event_length_in_hours) CHECK_FOR_ORGANISER_TIME_CHANGE() INT iDayIndex = ENUM_TO_INT(dayTarget) INT iHourIndex = hourTarget INT i = 0 REPEAT event_length_in_hours i IF iHourIndex > 23 iHourIndex = 0 ++iDayIndex ENDIF IF iDayIndex > 6 iDayIndex = 0 ENDIF g_OrganiserUpcomingWeek[iDayIndex][iHourIndex].type = event_type g_iOrganiserEventsCurrentlyLogged++ PRINTSTRING("PLACE_EVENT_IN_ABSOLUTE_ORGANISER_TIMESLOT: Writing to hour : ") PRINTINT(iHourIndex) PRINTNL() ++iHourIndex ENDREPEAT PRINTSTRING("PLACE_EVENT_IN_ABSOLUTE_ORGANISER_TIMESLOT: Event written to log day index : ") PRINTINT(iDayIndex) PRINTNL() ENDPROC //public functions to be supported //HOURS UNTIL NEXT EVENT /// PURPOSE: /// Find the next event and the number of hours until it /// PARAMS: /// hours_out - the number of hours until the next event, if no events at all then -1 /// RETURNS: /// The type enum of the next event FUNC ORGANISER_EVENT_ENUM HOURS_UNTIL_NEXT_EVENT(INT &hours_out) //TODO account for starting in an event //g_iFocusHour //g_iFocusDay INT i = 0 INT iDayIndex = g_iFocusDay INT iHourIndex = g_iFocusHour //find the first event that REPEAT (7*24) i IF iHourIndex > 23 iHourIndex = 0 ++iDayIndex ENDIF IF iDayIndex > 6 iDayIndex = 0 ENDIF IF NOT (g_OrganiserUpcomingWeek[iDayIndex][iHourIndex].type = ORG_EVT_NOTHING) hours_out = (i) RETURN g_OrganiserUpcomingWeek[iDayIndex][iHourIndex].type ENDIF ++iHourIndex ENDREPEAT hours_out = -1 RETURN ORG_EVT_NOTHING ENDFUNC /// PURPOSE: /// Returns the length from the selected point to the end of the event it is in /// PARAMS: /// iDay - The day to look in /// iHour - The hour slot to look in /// RETURNS: /// FUNC INT GET_LENGTH_OF_EVENT_IN_SLOT(INT iDay,INT iHour) IF g_OrganiserUpcomingWeek[iDay][iHour].type = ORG_EVT_NOTHING SCRIPT_ASSERT("GET_LENGTH_OF_EVENT_IN_SLOT: Attempting to check the length of an ORG_EVT_NOTHING event!") PRINTSTRING("GET_LENGTH_OF_EVENT_IN_SLOT: Attempting to check the length of an ORG_EVT_NOTHING event!") RETURN -1 ENDIF ORGANISER_EVENT_ENUM looking_for_type = g_OrganiserUpcomingWeek[iDay][iHour].type INT i = 0 INT iDayCaret = iDay INT iHourCaret = iHour REPEAT (7*24) i IF iHourCaret > 23 iHourCaret = 0 ++iDayCaret ENDIF IF iDayCaret > 6 iDayCaret = 0 ENDIF //look for the first index that is not of this event type IF NOT (looking_for_type = g_OrganiserUpcomingWeek[iDayCaret][iHourCaret].type) RETURN i//number of hours this event went on unbroken ENDIF ++iHourCaret ENDREPEAT PRINTSTRING("GET_LENGTH_OF_EVENT_IN_SLOT: Event was 24*7 hours long or greater, was this intended?") PRINTNL() RETURN (7*24) //event never ended, wtf ENDFUNC /// PURPOSE: /// Finds the next event of the given type (from the current time) and returns the hours until and how long it lasts /// PARAMS: /// type - the type of event to look for /// hours_until - the hours from the current time until this event /// lasts_for_hours - how long this event has to run PROC RETRIEVE_DETAILS_FOR_NEXT_EVENT_OF_TYPE(ORGANISER_EVENT_ENUM type, INT &hours_until,INT &lasts_for_hours) INT i = 0 INT iDayIndex = g_iFocusDay INT iHourIndex = g_iFocusHour //find the first event thatis of the type requested REPEAT (7*24) i IF iHourIndex > 23 iHourIndex = 0 ++iDayIndex ENDIF IF iDayIndex > 6 iDayIndex = 0 ENDIF IF g_OrganiserUpcomingWeek[iDayIndex][iHourIndex].type = type //found the right event! hours_until = i lasts_for_hours = GET_LENGTH_OF_EVENT_IN_SLOT(iDayIndex,iHourIndex) EXIT ENDIF ++iHourIndex ENDREPEAT //otherwise fail conditions hours_until = -1 lasts_for_hours = -1 ENDPROC /// PURPOSE: /// Returns the event enum for the event in the specified slot /// PARAMS: /// day - the day to check /// hour - the hour to check /// RETURNS: /// The event type enum contained FUNC ORGANISER_EVENT_ENUM GET_EVENT_AT(INT day,INT hour) IF day < 0 OR hour < 0 OR hour > 23 OR day > 6 RETURN ORG_EVT_NOTHING ENDIF IF NOT (g_OrganiserUpcomingWeek[day][hour].type = ORG_EVT_NOTHING) PRINTSTRING("Event returned!") PRINTNL() ENDIF RETURN g_OrganiserUpcomingWeek[day][hour].type ENDFUNC /// PURPOSE: /// Spams the contents of the organiser to the debug output PROC DEBUG_PRINT_INT_CONTENTS_OF_ORGANISER_TYPES() INT i = 0 INT j = 0 PRINTSTRING("DEBUG_PRINT_INT_CONTENTS_OF_ORGANISER_TYPES : Outputting week data spam") PRINTNL() REPEAT 7 i PRINTSTRING("Day divider") PRINTNL() REPEAT 24 j PRINTINT(ENUM_TO_INT(g_OrganiserUpcomingWeek[i][j].type)) IF (g_iFocusDay = i) AND (g_iFocusHour = j) PRINTSTRING("<-Caret is here") ENDIF PRINTNL() ENDREPEAT ENDREPEAT PRINTSTRING("DEBUG_PRINT_INT_CONTENTS_OF_ORGANISER_TYPES : Week data spam over") PRINTNL() //IF NOT (g_OrganiserUpcomingWeek[g_iFocusDay][g_iFocusHour].type = ORG_EVT_NOTHING) PRINTSTRING("Time until next event from caret: ") INT hours_until = 0 ORGANISER_EVENT_ENUM nextevent = HOURS_UNTIL_NEXT_EVENT(hours_until) PRINTINT(hours_until) PRINTSTRING(" and is of type ") PRINTINT(ENUM_TO_INT(nextevent)) PRINTNL() //ENDIF ENDPROC /// PURPOSE: /// Takes the ID of the movie that contains the organiser functionality /// PARAMS: /// movid - PROC UPDATE_ORGANISER_SCALEFORM(SCALEFORM_INDEX movid, BOOL forceupdate = FALSE) //check if changes have been made since the last time this was called //if not then bail PRINTLN("UPDATE_ORGANISER_SCALEFORM : Starting\n") IF g_iOrganiserChangeValue = g_iOrganiserChangeValueLastUpdate //no changes to update, don't bother IF NOT forceupdate PRINTLN("UPDATE_ORGANISER_SCALEFORM : No changes bailing\n") EXIT ENDIF ENDIF //check if the movie is valid IF (movid = NULL) //TODO assert PRINTLN("UPDATE_ORGANISER_SCALEFORM : Organiser movie invalid.\n") EXIT ENDIF IF NOT HAS_SCALEFORM_MOVIE_LOADED(movid) //TODO assert PRINTLN("UPDATE_ORGANISER_SCALEFORM : Orginiser movie not loaded\n") EXIT ENDIF CHECK_FOR_ORGANISER_TIME_CHANGE() //clear the entries //add each consecutive block to the organiser //ORGANISER_EVENT_STRUCT g_OrganiserUpcomingWeek[7][24] INT d = g_iFocusDay INT h = g_iFocusHour BOOL iterating = TRUE ORGANISER_EVENT_ENUM lookingat = ORG_EVT_NOTHING ORGANISER_EVENT_ENUM lastlookingat = ORG_EVT_NOTHING INT slot = 0 //GET_LENGTH_OF_EVENT_IN_SLOT(INT iDay,INT iHour) PRINTLN("UPDATE_ORGANISER_SCALEFORM : Starting iteration\n") WHILE iterating lastlookingat = lookingat lookingat = g_OrganiserUpcomingWeek[d][h].type //PRINTSTRING("Looking at index : d ") //PRINTINT(d) //PRINTSTRING(" : h ") //PRINTINT(h) //PRINTNL() IF NOT (lookingat = lastlookingat) IF NOT (lookingat = ORG_EVT_NOTHING) //SET_DATA_SLOT(5, 0, 14, 30, "Tuesday", "Meeting with Franklin"); STRING day //figure out what day it is SWITCH d CASE 0 //sunday day = "fSu" BREAK CASE 1 //monday day = "fMo" BREAK CASE 2 //tuesday day = "fTu" BREAK CASE 3 //wednesday day = "fWe" BREAK CASE 4 //thursday day = "fTh" BREAK CASE 5 //friday day = "fFr" BREAK CASE 6 //saturday day = "fSa" BREAK ENDSWITCH PRINTSTRING("Printing day : " ) PRINTINT(d) PRINTNL() //CALL_SCALEFORM_MOVIE_METHOD_WITH_NUMBER(movid, "SET_DATA_SLOT_EMPTY", TO_FLOAT(slot)) BEGIN_SCALEFORM_MOVIE_METHOD(movid, "SET_DATA_SLOT_EMPTY") SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(slot) END_SCALEFORM_MOVIE_METHOD() /* CALL_SCALEFORM_MOVIE_METHOD_WITH_NUMBER_AND_STRING(movid, "SET_DATA_SLOT", 5.0, TO_FLOAT(slot), TO_FLOAT(h), 0.0, -1.0, day, GET_ORGANISER_CATEGORY_STRING(lookingat)) */ BEGIN_SCALEFORM_MOVIE_METHOD(movid, "SET_DATA_SLOT") SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(5) SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(slot) SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(h) SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(0.0) SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(-1.0) SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(day) SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(GET_ORGANISER_CATEGORY_STRING(lookingat)) END_SCALEFORM_MOVIE_METHOD() slot++ ENDIF ENDIF //"BSCO_MAI_S" h++ IF h > 23 //hour has looped h = 0 d++ ENDIF IF d > 6 //day has looped d = 0 ENDIF IF (d = g_iFocusDay) AND (h = g_iFocusHour) //iteration complete iterating = FALSE ENDIF ENDWHILE PRINTLN("UPDATE_ORGANISER_SCALEFORM : Iteration complete\n") g_iOrganiserChangeValueLastUpdate = g_iOrganiserChangeValue ENDPROC