@q @edit jweather.muf 1 9999 d i ( jweather.muf v1.1 Jessy @ FurryMUCK 7/01 An easy to set up, multi-climate MUCK weather system. Jweather supports both periodic weather messages to rooms and weather-related MPI. INSTALLATION: Ensure that the program is set at least M3. Create a global exit with a name such as 'weather' and link it to the program. Internal routines will complete the installation the first time the program is run. CONFIGURATION: By default, jweather uses values for the northern hemisphere and measurements in fahrenheit and imperial units. To change this, edit the definitions below for HEMISPHERE and SCALE, and recompile the program.) (* edit HEMISPHERE value to "northern" or "southern" *) $define HEMISPHERE "northern" $enddef (* edit SCALE value to "fahrenheit" or "centigrade" *) $define SCALE "fahrenheit" $enddef ( Jweather installs one default climate, named 'Temperate'. You can define additional climates -- with associated values for high and low temperatures, changeability, etc. -- and/or change the default values for the Temperate climate with the command's #config option. To set the climate for a specific room, go to the room and use the #climate option. To set the climate for an area, go to the area's environment room and use the #climate option. To cause periodic weather messages to be displayed in a specific room, go to the room and use the #yes option. To cause periodic messages to be displayed in all rooms in an area, go to the area's environment room and use the #yes option. MPI: Jweather supports the following MPI functions: {weather:climate} ..... <climate name for current location> {weather:daynight} .... day|night {weather:degrees} ..... <temperature in degrees fahrenheit> {weather:moonphase} ... full|gibbous|half|quarter|new {weather:phase} ....... morning|afternoon|evening|night {weather:precip} ...... rain|mist|snow|hail|"" {weather:precipverb} .. raining|misting|snowing|hailing|"" {weather:temp} ........ extremely cold|very cold|cold|cool| warm|hot|very hot|extremely hot {weather:season} ...... spring|summer|fall|winter {weather:skyadj} ...... overcast|mostly cloudy|partly cloudy|clear {weather:skynoun} ..... clouds|some clouds|a few clouds|no clouds {weather:waxwane} ..... waxing|waning {weather:windadj} ..... calm|breezy|windy|very windy|extremely windy {weather:winddir} ..... north|northeast|east|southeast|south| southwest|west|northwest {weather:winddiradj} .. northerly|northeasterly|easterly|southeasterly| southwesterly|westerly|northwesterly {weather:windnoun} .... light winds|moderate winds|high winds| extremely high winds|gail force winds {weather:windspeed} ... <speed> mph|kph Jweather.muf may be freely ported. Please comment any changes. CHANGES: 1.1: Fixed a major problem with the {weather} macro. ) (2345678901234567890123456789012345678901234567890123456789012345678901) $define Tell me @ swap notify $enddef $define DoNukeStack begin depth while pop repeat $enddef $include $lib/reflist lvar ourArg lvar ourDir lvar ourOpt lvar ourLoc lvar ourCounter lvar ourClimate : CopyDir ( d1 s1 d2 s2 -- ) (* copy dir s1 on d1 to dir s2 on d2. do not copy subdirs *) 4 pick 4 pick propdir? if 3 pick "*/" smatch not if 3 pick "/" strcat 3 put then else pop pop pop pop exit then dup "*/" smatch not if "/" strcat then 3 pick 5 rotate 5 rotate 5 rotate 5 rotate dup 5 rotate 5 rotate 5 rotate 5 rotate 4 pick 4 pick nextprop dup 4 put 5 rotate 5 rotate 5 rotate 5 rotate begin 4 pick 4 pick getprop if pop over 7 pick 7 pick swap subst 4 pick 4 pick 4 pick 4 pick 4 rotate 4 rotate getprop setprop 4 pick 4 pick nextprop dup not if break then dup 4 put 5 put else 4 pick 4 pick dup "*/" smatch if dup strlen 1 - strcut pop then over over nextprop not if pop pop break then nextprop dup 4 put 5 put then pop over 7 pick 7 pick swap subst repeat pop pop pop pop pop pop pop pop ; : DoParseThis ( d s -- s ) (* returns d's prop s, parsed for MPI *) dup 3 pick swap getpropstr 0 parseprop ; : DoParseTimeString ( s -- i1 i2 | i ) (* convert string s to number of seconds i1. i2 is true if successful *) (* format of s is '<num> <units>', eg '3 hours', '1 day', '2 weeks' *) (* if unsuccessful, return only one val, 0 *) (* tokenize string *) " " explode dup 2 = if (* check syntax and bail out if needed *) pop else begin dup while swap pop 1 - repeat pop ">> Entry not understood." Tell 0 exit then (* parse units and convert amount *) swap strip "seconds" over stringpfx if 1 else "minutes" over stringpfx if 60 else "hours" over stringpfx if 3600 else "days" over stringpfx if 86400 else "weeks" over stringpfx if 604800 else "months" over stringpfx if 1036800 else "years" over stringpfx if 12441600 else pop pop 0 exit then then then then then then then swap pop swap atoi * 1 ; : DoCapRomans ( s -- s' ) (* return s, all caps if it's a low roman *) dup "{ii|iii|iv|v|vi|vii|viii|ix}" smatch if toupper then ; : DoCapitalize ( s -- s' ) (* return s, capitalized *) 1 strcut swap toupper swap strcat DoCapRomans ; : DoCapAll ( s -- s' ) (* return s, all words upper case *) " " explode dup if "" begin rot DoCapitalize " " strcat strcat swap 1 - swap over while repeat swap pop dup strlen 1 - strcut pop else pop then ; : DoApplyTempVals ( -- )(* move working dir to dir for new climate *) prog ourDir @ prog "climates/%name/" ourClimate @ "%name" subst CopyDir prog ourDir @ dup strlen 1 - strcut pop remove_prop ; : DoReadLine ( -- s ) (* read keyboard input; emit poses|says and continue, else return *) begin (* begin input-scanning loop *) read (* does input begin with 'say ' or " ? Emit if so *) dup "\"" stringpfx if 1 strcut swap pop me @ name " says, \"" strcat swap strcat "\"" strcat loc @ swap 0 swap notify_exclude continue then dup "say " stringpfx if 4 strcut swap pop me @ name " says, \"" strcat swap strcat "\"" strcat loc @ swap 0 swap notify_exclude continue then (* does input begin with 'pose ' or : ? Emit if so *) dup ":" stringpfx if 1 strcut swap pop me @ name " " strcat swap strcat loc @ swap 0 swap notify_exclude continue then dup "pose " stringpfx if 5 strcut swap pop me @ name " " strcat swap strcat loc @ swap 0 swap notify_exclude continue then (* continue for strings of all spaces; i.e., treat as null *) dup strip not if pop continue then break (* it's not a pose or say; break and exit *) repeat ; : QCheck ( -- i )(* wrap smatch for .q in an if, to avoid null string match error if user enters a string of all spaces, which DoReadLine would strip to a null string *) dup if dup ".quit" swap stringpfx over ".end" swap stringpfx or if pop ">> Done." Tell pid kill then then ; : DoReadYesNo ( -- i ) (* read from keyboard; accept only vars of yes|no; return 1 for yes *) begin (* begin input-scanning loop *) DoReadLine QCheck "yes" over stringpfx if pop 1 break then "no" over stringpfx if pop 0 break then pop ">> Please enter 'Yes' or 'No'." Tell repeat (* end input-scanning loop *) ; : DoGetFirst ( -- s ) (* can't find climate; use first def'd *) prog "climates/" nextprop dup if "" "climates/" subst else (* shouldn't happen *) ">> ERROR: Weather system not properly initialized." Tell pop pid kill then ; : DoGetDefaultClimate ( -- s ) (* return def'd default climate name *) prog "config/default" getpropstr dup not if pop DoGetFirst (* use first climate in propdir if no default *) then DoCapAll ; : DoGetClimate ( -- s ) (* return climate name for ourLoc *) (* prop search first.... *) ourLoc @ "weather/climate" envpropstr swap pop (* use default if not found by prop *) dup not if pop DoGetDefaultClimate then (* make sure found climate actually exists; use default if not *) prog "climates/%clim/" 3 pick "%clim" subst nextprop not if pop DoGetDefaultClimate then ; : DoGetMoonPhase ( -- s ) (* return current phase of moon *) systime 991983670 - (* get time since a full moon in June 2001 *) 2360595 % (* divide by moon periods; get remainder *) 86400 / (* divide into days *) 1 over >= if "full" else (* calc phase as offset from full *) 6 over >= if "gibbous" else 8 over >= if "half" else 12 over >= if "quarter" else 14 over >= if "new" else 20 over >= if "quarter" else 22 over >= if "half" else 26 over >= if "gibbous" else "full" then then then then then then then then swap pop ; : DoGetWaxWane ( -- s ) (* return wax|wane *) DoGetMoonPhase "{full|new}" smatch if "" exit then systime 991810870 - 2360595 % 86400 / 14 over >= if pop "waning" else "waxing" then ; : DoGetSeason ( -- ) (* return season of year *) "%m" systime timefmt atoi HEMISPHERE "northern" smatch if dup 2 <= if pop "winter" exit then dup 5 <= if pop "spring" exit then dup 8 <= if pop "summer" exit then dup 11 <= if pop "fall" exit then pop "winter" else dup 2 <= if pop "summer" exit then dup 5 <= if pop "fall" exit then dup 8 <= if pop "winter" exit then dup 11 <= if pop "spring" exit then pop "summer" then ; : DoGetDayPhase ( -- s )(* return time of day: morning, night, etc. *) "%k" systime timefmt atoi dup 6 < if pop "night" exit then dup 12 < if pop "morning" exit then dup 18 < if pop "afternoon" exit then dup 20 < if pop "evening" exit then pop "night" exit ; : DoGetDayNight ( -- s )(* return 'day' for 0600-1800, else 'night' *) "%k" systime timefmt atoi dup 6 < swap 18 > or if "night" else "day" then ; : DoApplyNightCooling ( s -- s' ) (* cool it down some at night *) DoGetDayPhase dup "night" smatch if pop atoi SCALE "fahrenheit" smatch if 10 else 5 then - intostr else "evening" smatch if atoi SCALE "fahrenheit" smatch if 5 else 2 then - intostr then then ; : DoGetTemperature ( -- s ) (* return current temp in degrees *) prog "conditions/%clim/temp" ourClimate @ "%clim" subst getpropstr DoApplyNightCooling ; : DoGetFreezing ( -- s ) (* return true if it's frezzing in ourClimate *) SCALE "fahrenheit" smatch if 32 else 0 then DoGetTemperature atoi >= if "yes" else "" then ; : DoGetPrecipitation ( -- s ) (* return true if it is precipitating in ourClimate *) prog "conditions/%clim/prec" ourClimate @ "%clim" subst getpropstr ; : DoGetPrecipNoun ( -- s ) (* return current form of precipitation *) DoGetPrecipitation if prog "conditions/%clim/prcf" ourClimate @ "%clim" subst getpropstr else "" then ; : DoGetPrecipVerb ( -- s ) (* return verb for current precip form *) DoGetPrecipNoun dup if "ing" strcat then ; : DoGetTempAdj ( -- s ) (* return adj describing current temp *) DoGetTemperature atoi SCALE "fahrenheit" smatch if 0 over >= if pop "extremely cold" else 32 over >= if pop "very cold" else 45 over >= if pop "cold" else 60 over >= if pop "cool" else 78 over >= if pop "warm" else 90 over >= if pop "hot" else 100 over >= if pop "very hot" else pop "extremely hot" then then then then then then then else -17 over >= if pop "extremely cold" else 0 over >= if pop "very cold" else 7 over >= if pop "cold" else 16 over >= if pop "cool" else 26 over >= if pop "warm" else 32 over >= if pop "hot" else 38 over >= if pop "very hot" else pop "extremely hot" then then then then then then then then ; : DoGetSkyAdj ( -- s ) (* return adj describing current cloud cond *) prog "conditions/%clim/over" ourClimate @ "%clim" subst getpropstr ; : DoGetSkyNoun ( -- s ) (* return noun for current cloud conditions *) DoGetSkyAdj "overcast" over smatch if "clouds" else "mostly cloudy" over smatch if "some clouds" else "partly cloudy" over smatch if "a few clouds" else "no clouds" then then then swap pop ; : DoGetWindSpeed ( -- s ) (* return current wind speed in mph|kph *) prog "conditions/%clim/wnds" ourClimate @ "%clim" subst getpropstr " " strcat SCALE "fahrenheit" smatch if "mph" else "kph" then strcat ; : DoGetWindDir prog "conditions/%clim/wndd" ourClimate @ "%clim" subst getpropstr "0" over smatch if "north" else "1" over smatch if "northeast" else "2" over smatch if "east" else "3" over smatch if "southeast" else "4" over smatch if "south" else "5" over smatch if "southwest" else "6" over smatch if "west" else "7" over smatch if "northwest" else "north" then then then then then then then then swap pop ; : DoGetWindAdj ( -- s ) (* return an adj describing current windspd *) prog "conditions/%clim/wnds" ourClimate @ "%clim" subst getpropstr atoi SCALE "centigrade" smatch if 2 / then dup 2 <= if "calm" else dup 8 <= if "breezy" else dup 17 <= if "windy" else dup 25 <= if "very windy" else "extremely windy" then then then then swap pop ; : DoGetWindDirAdj ( -- s ) (* return an adv describing wind dir *) DoGetWindDir "erly" strcat ; : DoGetWindNoun ( -- s ) (* return wind speed as a noun *) prog "conditions/%clim/wnds" ourClimate @ "%clim" subst getpropstr atoi SCALE "centigrade" smatch if 2 / then dup 2 <= if "calm winds" else dup 8 <= if "light winds" else dup 14 <= if "moderate winds" else dup 25 <= if "high winds" else dup 60 <= if "extremely high winds" else "gail force winds" then then then then then swap pop ; : DoGetNormalTemp ( -- i ) (* get norm temp for clim & season *) DoGetSeason "summer" over smatch if prog "climates/%clim/nsht" ourClimate @ "%clim" subst getpropstr else "fall" over smatch if prog "climates/%clim/nsht" ourClimate @ "%clim" subst getpropstr atoi 1000 + dup prog "climates/%clim/nwlt" ourClimate @ "%clim" subst getpropstr atoi 1000 + - 2 / - 1000 - intostr else "winter" over smatch if prog "climates/%clim/nwlt" ourClimate @ "%clim" subst getpropstr atoi prog "climates/%clim/ndnd" ourClimate @ "%clim" subst getpropstr atoi + intostr else prog "climates/%clim/nsht" ourClimate @ "%clim" subst getpropstr atoi 1000 + dup prog "climates/%clim/nwlt" ourClimate @ "%clim" subst getpropstr atoi 1000 + - 2 / - 1000 - intostr then then then swap pop ; : DoGetRandomTemp ( -- s ) (* get random temp for season & clim *) DoGetNormalTemp atoi random SCALE "Fahrenheit" smatch if 10 else 5 then % 1 + random 2 % if + else - then prog "climates/%clim/esht" ourClimate @ "%clim" subst getpropstr atoi over < if pop prog "climates/%clim/esht" ourClimate @ "%clim" subst getpropstr then prog "climates/%clim/ewlt" ourClimate @ "%clim" subst getpropstr atoi over > if pop prog "climates/%clim/ewlt" ourClimate @ "%clim" subst getpropstr then dup string? not if intostr then ; : DoGetRandomPrecip ( -- s ) (* return precip T|F for ourClimate *) prog "climates/%clim/chng" ourClimate @ "%clim" subst getpropstr atoi random 100 % 1 + >= if "yes" else "" then ; : DoGetRandomPrecForm ( -- s ) (* return random precip form *) DoGetFreezing if random 100 % 1 + 15 >= if "snow" else "hail" then else random 100 % 1 + 15 >= if "rain" else "mist" then then ; : DoGetRandomWindDir ( -- s ) (* return random wind direction *) random 8 % intostr ; : DoGetRandomWindSpeed ( -- s ) (* return low random wind dir *) random 16 % intostr (* 0-15 ought to do it *) ; : DoGetRandomOverCast ( -- s ) (* return random overcast val *) DoGetPrecipitation if (* if it's raining, make it overcast *) 3 else (* otherwise, roll random *) random 4 % then dup 3 = if "overcast" else dup 2 = if "mostly cloudy" else dup 1 = if "partly cloudy" else "clear" then then then swap pop ; : DoChange? ( -- i ) (* make a roll; return true if weather changes *) random 100 % 1 + ourCounter @ <= if 1 else 0 then ; : DoUpdateWeather ( s -- ) (* check for changes in weather for climate stored in dir s *) "" "climates/" subst ourClimate ! (* store chance for changes *) prog "climates/%clim/chng" ourClimate @ "%clim" subst getprop atoi ourCounter ! (* rec current temp and precip, so we can see if changed *) prog "conditions/%clim/otmp" ourClimate @ "%clim" subst prog "conditions/%clim/temp" ourClimate @ "%clim" subst getprop setprop prog "conditions/%clim/oprc" ourClimate @ "%clim" subst prog "conditions/%clim/prec" ourClimate @ "%clim" subst getprop setprop (* if temp changes, move it up or down by a bit, randomly *) DoChange? if prog "conditions/%clim/temp" ourClimate @ "%clim" subst over over getprop atoi random SCALE "fahrenheit" smatch if 10 else 5 then % 1 + random 2 % if + else - then prog "climates/%clim/esht" ourClimate @ "%clim" subst getpropstr atoi dup 3 pick > if pop else swap pop then prog "climates/%clim/ewlt" ourClimate @ "%clim" subst getpropstr atoi dup 3 pick < if pop else swap pop then DoGetSeason "summer" smatch if prog "climates/%clim/esht" ourClimate @ "%clim" subst getpropstr atoi SCALE "Fahrenheit" smatch if 50 else 25 then - over over > if pop else swap pop then then DoGetSeason "winter" smatch if prog "climates/%clim/ewlt" ourClimate @ "%clim" subst getpropstr atoi SCALE "Fahrenheit" smatch if 50 else 25 then + over over < if pop else swap pop then then intostr setprop then (* if precip changes, toggle it *) DoChange? if prog "conditions/%clim/prec" ourClimate @ "%clim" subst over over getprop not setprop then (* see if we have unusual precip *) DoChange? if prog "conditions/%clim/prcf" ourClimate @ "%clim" subst DoGetFreezing if random 10 % 8 <= if "snow" else "hail" then else random 10 % 8 <= if "rain" else "mist" then then setprop then (* if windspeed changes, move it up or down by a bit *) DoChange? if prog "conditions/%clim/wnds" ourClimate @ "%clim" subst over over getprop atoi random 10 % 1 + random 2 % if + else - then 0 over > if pop 0 then intostr setprop then (* if wind direction changes, move it clockwise or counter by a bit *) DoChange? if prog "conditions/%clim/wndd" ourClimate @ "%clim" subst over over getprop atoi 1 random 2 % if + else - then 0 over > if pop 7 then 7 over < if pop 0 then intostr setprop then ; : DoInitConditions ( -- ) (* set initial conditions for ourClimate *) prog "conditions/%clim/init" ourClimate @ "%clim" subst DoGetSeason setprop prog "conditions/%clim/temp" ourClimate @ "%clim" subst prog "conditions/%clim/otmp" ourClimate @ "%clim" subst DoGetRandomTemp ourCounter ! ourCounter @ setprop ourCounter @ setprop prog "conditions/%clim/prec" ourClimate @ "%clim" subst prog "conditions/%clim/oprc" ourClimate @ "%clim" subst DoGetRandomPrecip ourCounter ! ourCounter @ setprop ourCounter @ setprop prog "conditions/%clim/prcf" ourClimate @ "%clim" subst DoGetRandomPrecForm setprop prog "conditions/%clim/wndd" ourClimate @ "%clim" subst DoGetRandomWindDir setprop prog "conditions/%clim/wnds" ourClimate @ "%clim" subst DoGetRandomWindSpeed setprop prog "conditions/%clim/over" ourClimate @ "%clim" subst DoGetRandomOverCast setprop ; : DoInstall ( -- ) (* set initial program props *) #0 "_reg/jweather" prog setprop prog "L" set #0 "_msgmacs/weather" "{muf:$jweather,{subst:{:1},{:1},#mpi{:1}}}" setprop prog "msgmacs/climate" "{muf:$jweather,#mpiclimate}" setprop prog "msgmacs/daynight" "{muf:$jweather,#mpidaynight}" setprop prog "msgmacs/degrees" "{muf:$jweather,#mpidegrees}" setprop prog "msgmacs/moonphase" "{muf:$jweather,#mpimoonphase}" setprop prog "msgmacs/phase" "{muf:$jweather,#mpiphase}" setprop prog "msgmacs/precip" "{muf:$jweather,#mpiprecip}" setprop prog "msgmacs/precipverb" "{muf:$jweather,#mpiprecipverb}" setprop prog "msgmacs/temp" "{muf:$jweather,#mpitemp}" setprop prog "msgmacs/season" "{muf:$jweather,#mpiseason}" setprop prog "msgmacs/skyadj" "{muf:$jweather,#mpiskyadj}" setprop prog "msgmacs/skynoun" "{muf:$jweather,#mpiskynoun}" setprop prog "msgmacs/waxwane" "{muf:$jweather,#mpiwaxwane}" setprop prog "msgmacs/windadj" "{muf:$jweather,#mpiwindadj}" setprop prog "msgmacs/winddir" "{muf:$jweather,#mpiwinddir}" setprop prog "msgmacs/winddiradj" "{muf:$jweather,#mpiwinddiradj}" setprop prog "msgmacs/windnoun" "{muf:$jweather,#mpiwindnoun}" setprop prog "msgmacs/windspeed" "{muf:$jweather,#mpiwindspeed}" setprop prog "climates/Temperate/chng" "25" setprop prog "climates/Temperate/esht" "102" setprop prog "climates/Temperate/ewlt" "0" setprop prog "climates/Temperate/ndnd" "16" setprop prog "climates/Temperate/nsht" "95" setprop prog "climates/Temperate/nwlt" "28" setprop prog "climates/Temperate/rain" "25" setprop prog "config/default" "Temperate" setprop ; : DoChecks ( -- ) (* make sure program and vars are iniitialized *) "me" match me ! (* to catch dbref spoofing *) loc @ ourLoc ! (* make a loc var we can safely fool with *) #0 "_msgmacs/weather" getpropstr not (* make sure props are set *) prog "msgmacs/" nextprop not or prog "climates/" nextprop not or if DoInstall then DoGetClimate ourClimate ! prog "conditions/%clim/init" ourClimate @ "%clim" subst getpropstr DoGetSeason stringcmp if DoInitConditions then ; : DoMPIHelp ( -- ) (* display MPI help screen *) "Jweather supports the following MPI functions:" Tell " " Tell "{weather:windspeed} ... <speed> %units" SCALE "fahrenheit" smatch if "mph" else "kph" then "%units" subst " extremely high winds|gail force winds" "{weather:windnoun} .... light winds|moderate winds|high winds|" " southwesterly|westerly|northwesterly" "{weather:winddiradj} .. northerly|northeasterly|easterly|southeasterly|" " southwest|west|northwest" "{weather:winddir} ..... north|northeast|east|southeast|south|" "{weather:windadj} ..... calm|breezy|windy|very windy|extremely windy" "{weather:waxwane} ..... waxing|waning" "{weather:skynoun} ..... clouds|some clouds|a few clouds|no clouds" "{weather:skyadj} ...... overcast|mostly cloudy|partly cloudy|clear" "{weather:season} ...... spring|summer|fall|winter" " warm|hot|very hot|extremely hot" "{weather:temp} ........ extremely cold|very cold|cold|cool|" "{weather:precipverb} .. raining|misting|snowing|hailing|\"\"" "{weather:precip} ...... rain|mist|snow|hail|\"\"" "{weather:phase} ....... morning|afternoon|evening|night" "{weather:moonphase} ... full|gibbous|half|quarter|new" "{weather:degrees} ..... <temperature in degrees %scale>" SCALE tolower "%scale" subst "{weather:daynight} .... day|night" "{weather:climate} ..... <climate name for current location>" " FUNCTION RETURNS" Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell Tell ; : DoHelp ( -- ) (* display help screen *) ourArg @ if "mpi" ourArg @ stringpfx "#mpi" ourArg @ stringpfx or if DoMPIHelp exit then then "jweather.muf (#" prog intostr strcat ")" strcat Tell " " Tell "Jweather is a MUCK weather system. It can be used to display " "notifications of weather changes to rooms, or to support weather-" "related MPI." strcat strcat Tell " " Tell " %cmd ................... Display current temp and precipitation" command @ "%cmd" subst Tell " %cmd #full ............. Display full weather report" command @ "%cmd" subst Tell " %cmd #climate .......... Display name of current climate" command @ "%cmd" subst Tell " %cmd #climate <name> ... Set climate for current room (o)" command @ "%cmd" subst Tell " %cmd #yes .............. Current room will display weather notices (o)" command @ "%cmd" subst Tell " %cmd #no ............... Current room won't display weather notices (o)" command @ "%cmd" subst Tell " %cmd #start ............ Start weather system (w)" command @ "%cmd" subst Tell " %cmd #stop ............. Stop weather system (w)" command @ "%cmd" subst Tell " %cmd #config ........... Go to prompt; configure system (w)" command @ "%cmd" subst Tell " " Tell "You must be a wizard or the owner of the current room to use options " "marked (o). You must be a wizard to use options marked (w). To cause " "weather notices to go to all rooms in an area, use the #yes option " "in the area environment room. To set the climate for all rooms in " "an area, use the #climate option in the area environment room. For " "help on using MPI with jweather, type '%cmd #help mpi'." strcat strcat strcat strcat strcat command @ "%cmd" subst Tell ; : DoListClimates ( -- ) (* list defined climates *) ">> DEFINED CLIMATES:" Tell prog "climates/" nextprop dup if begin dup while " " over "" "climates/" subst strcat Tell prog swap nextprop repeat pop else " <none>" Tell then ; : DoClimate ( -- ) (* set the current location's climate *) ourArg @ if me @ loc @ controls if ourArg @ DoCapAll ourArg ! prog "climates/%clim/" ourArg @ "%clim" subst nextprop if loc @ "weather/climate" ourArg @ setprop ">> Climate set." Tell else " " Tell DoListClimates " " Tell ">> No climate called '%clim' has been defined." ourArg @ "%clim" subst Tell then else ">> Permission denied." Tell then else ">> The climate at this location is %clim." DoGetClimate "%clim" subst Tell then ; : DoRoomMessage ( d -- ) (* display weather message to room d *) (* make sure messages haven't been quelled for d *) dup "weather/quell" getpropstr if pop exit then (* store room to notify *) ourLoc ! (* store dir holding room's climate's conditions *) DoGetClimate "conditions/" swap strcat "/" strcat ourDir ! (* start with a null string; won't notify if no changes *) "" (* if temp changed, format that as a weather notice; concat *) prog ourDir @ "temp" strcat getprop atoi prog ourDir @ "otmp" strcat getprop atoi = not if "The temperature %verbs." prog ourDir @ "temp" strcat getprop atoi prog ourDir @ "otmp" strcat getprop atoi > if "rises" else "falls" then "%verbs" subst strcat then (* if precip changed, format that as a weather notice; concat *) prog ourDir @ "prec" strcat getprop atoi prog ourDir @ "oprc" strcat getprop atoi = not if prog ourDir @ "prec" strcat getprop if dup if ", and it begins to %verb." "." subst else "It begins to %verb." then else dup if ", and it stops %verbing." "." subst else "It stops %verbing." then then prog ourDir @ "prcf" strcat getprop "%verb" subst strcat then (* if we ended up with a message, display it to room *) dup if ourLoc @ #-1 rot notify_except else pop then ; : DoRoomLoop ( d -- ) (* display message to d; search for subrooms *) dup DoRoomMessage (* notify room *) contents begin dup while (* see if it holds child rooms *) dup room? if dup DoRoomLoop (* if so, call recursively *) then next (* check next contents item of room; is *it* a room? *) repeat pop ; : DoStartWeather ( -- ) (* start loop controlling weather system *) me @ "W" flag? not if (* check perm *) ">> Permission denied." Tell exit then background (* get out of the way *) DoChecks (* make sure we have all needed props *) prog "config/pid" getprop dup if (* get rid of any previous loop *) kill pop else pop then prog "config/pid" pid setprop (* record pid *) prog "config/stop" remove_prop ">> Weather system started." Tell (* notify wiz *) begin (* start weather loop *) prog "config/stop" getpropstr if (* check: told to stop? *) prog "config/stop" remove_prop prog "config/pid" getprop kill prog "config/pid" remove_prop pid kill then prog "climates/" nextprop (* update weather for each clim *) begin dup while dup DoUpdateWeather prog swap nextprop repeat pop prog "_rooms/" nextprop dup if (* notify config'd rooms *) "" "_rooms/" subst atoi dbref ourCounter ! begin ourCounter @ ok? if (* record if prop holds bad room *) ourCounter @ room? if ourCounter @ DoRoomLoop else prog "config/badrooms" ourCounter @ REF-add then else prog "config/badrooms" ourCounter @ REF-add then prog "_rooms/%room" ourCounter @ intostr "%room" subst nextprop dup if "" "_rooms/" subst atoi dbref ourCounter ! else pop break then repeat else pop then (* remove any rec'd rooms from prop list *) prog "config/badrooms" REF-allrefs begin dup while prog "_rooms/%room" 4 rotate intostr "%room" subst remove_prop 1 - repeat pop (* sleep for config'd interval, default 1 hour *) prog "config/interval" getpropstr dup if atoi else pop 3600 then sleep repeat ; : DoStopWeather ( -- ) (* stop the weather loop *) me @ "W" flag? not if ">> Permission denied." Tell exit then prog "config/pid" over over getprop kill pop remove_prop prog "config/stop" 1 setprop ">> Weather system stopped." Tell ; : DoSetYes ( -- ) (* config room to display weather notices *) me @ loc @ controls if prog "_rooms/" loc @ intostr strcat 1 setprop ">> Set. This room will display weather notifications." 0 ourCounter ! loc @ contents begin dup while dup room? if 1 ourCounter ! break then next repeat pop ourCounter @ if "room and its child rooms" "room" subst then Tell else ">> Permission denied." Tell then ; : DoSetNo ( -- ) (* config room to not display weather notices *) me @ loc @ controls if prog "_rooms/" loc @ intostr strcat remove_prop loc @ "weather/quell" "yes" setprop ">> Set. This room will not display weather notifications." Tell else ">> Permission denied." Tell then ; : DoShowClimateVals ( s -- ) (* show vals for ourClimate *) ">> CURRENT VALUES FOR CLIMATE %name" over toupper "%name" subst Tell ">> Normal Summer High ......... %val" prog "climates/%name/nsht" 4 pick "%name" subst getpropstr "%val" subst Tell ">> Extreme Summer High ........ %val" prog "climates/%name/esht" 4 pick "%name" subst getpropstr "%val" subst Tell ">> Normal Winter Low .......... %val" prog "climates/%name/nwlt" 4 pick "%name" subst getpropstr "%val" subst Tell ">> Extreme Winter Low ......... %val" prog "climates/%name/ewlt" 4 pick "%name" subst getpropstr "%val" subst Tell ">> Normal Day/Night Swing ..... %val" prog "climates/%name/ndnd" 4 pick "%name" subst getpropstr "%val" subst Tell ">> Rain Scale ................. %val" prog "climates/%name/rain" 4 pick "%name" subst getpropstr "%val" subst Tell ">> Changeability Scale ........ %val" prog "climates/%name/chng" 4 pick "%name" subst getpropstr "%val" subst Tell pop ; : DoSetNSHT ( -- ) (* set normal summer high temp for ourClimate *) begin ">> [Enter a normal summer high temperature, or .q to quit]" ">> In this climate, how hot does it normally get in the summer time?" Tell Tell DoReadLine strip QCheck dup number? not if ">> Sorry, a temperature entry must be a number." Tell pop continue then prog ourDir @ "nsht" strcat rot setprop break repeat ; : DoSetESHT ( -- )(* set extreme summer high temperature for ourClim *) begin ">> [Enter an extreme summer high temperature, or .q to quit]" ">> What is the hottest it ever gets during summer?" Tell Tell DoReadLine strip QCheck dup number? not if ">> Sorry, a temperature entry must be a number." Tell pop continue then prog ourDir @ "esht" strcat rot setprop break repeat ; : DoSetNWLT ( -- ) (* set normal low winter temp for ourClimate *) begin ">> [Enter a normal winter low temperature, or .q to quit]" ">> How cold does it normally get in the winter?" Tell Tell DoReadLine strip QCheck dup number? not if ">> Sorry, a temperature entry must be a number." Tell pop continue then prog ourDir @ "nwlt" strcat rot setprop break repeat ; : DoSetEWLT ( -- ) (* set extreme low winter temp for ourClimate *) begin ">> [Enter an extreme winter low temperature, or .q to quit]" ">> What is the coldest it ever gets during winter?" Tell Tell DoReadLine strip QCheck dup number? not if ">> Sorry, a temperature entry must be a number." Tell pop continue then prog ourDir @ "ewlt" strcat rot setprop break repeat ; : DoSetNDND ( -- ) (* set amount temp drops at night in ourClimate *) begin ">> [Enter the normal difference between daytime high and nighttime low]" ">> How much does the temperature drop at night?" Tell Tell DoReadLine strip QCheck dup number? not if ">> Sorry, a temperature entry must be a number." Tell pop continue then prog ourDir @ "ndnd" strcat rot setprop break repeat ; : DoSetRain ( -- ) (* set percent time it rains in ourClimate *) begin ">> [Enter rain scale as a number between 1 and 100, or .q to quit]" " is this climate?" ">> On a scale of 1 (never rains) to 100 (always rains), how rainy " Tell Tell Tell DoReadLine strip QCheck dup number? not if ">> Sorry, a scale entry must be a number between 1 and 100." Tell pop continue then dup atoi 100 > if ">> Sorry, a scale entry must be a number between 1 and 100." Tell pop continue then dup atoi 1 < if ">> Sorry, a scale entry must be a number between 1 and 100." Tell pop continue then prog ourDir @ "rain" strcat rot setprop break repeat ; : DoSetChng ( -- ) (* set percent chance of weather chng *) begin ">> [Enter changeability scale as a number between 1 and 100, or .q " "to quit]" strcat " constantly changing, how changeable is this climate?" ">> On a scale of 1 (weather always the same) to 100 (weather is " Tell Tell Tell DoReadLine strip QCheck dup number? not if ">> Sorry, a scale entry must be a number between 1 and 100." Tell pop continue then dup atoi 100 > if ">> Sorry, a scale entry must be a number between 1 and 100." Tell pop continue then dup atoi 1 < if ">> Sorry, a scale entry must be a number between 1 and 100." Tell pop continue then prog ourDir @ "chng" strcat rot setprop break repeat ; : DoAddClimate ( -- ) (* define a new climate *) (* store in temp dir so we won't have partial data if user bails *) "_temp/%me/" me @ intostr "%me" subst ourDir ! prog ourDir @ dup strlen 1 - strcut pop remove_prop begin (* get a name for climate *) ">> What is the name of this climate?" Tell ">> [Enter name of climate, or .q to quit]" Tell DoReadLine strip QCheck (* if climate exists, confirm overwrite *) prog "climates/%name/" 3 pick "%name" subst nextprop if ">> A climated named '%name' has already been defined." over "%name" subst Tell ">> Do you want to overwrite it? (y/n)" Tell DoReadYesNo not if ">> Aborted." Tell pop continue then then DoCapAll ourClimate ! break repeat DoSetNSHT (* set climate parameters *) DoSetESHT DoSetNWLT DoSetEWLT DoSetNDND DoSetRain DoSetChng DoApplyTempVals (* got all we need; move it to permanent data dir *) prog "config/default" getpropstr not if prog "config/default" ourClimate @ setprop then DoInitConditions (* initialize conditions for new climate *) ">> Climate %name defined!" ourClimate @ "%name" subst Tell ; : DoEditClimate ( -- ) (* edit params for an existing climate *) (* store in temp dir so we won't have partial data if user bails *) "_temp/%me/" me @ intostr "%me" subst ourDir ! prog ourDir @ dup strlen 1 - strcut pop remove_prop begin ">> [Enter climate name, or .l to list choices, or .q to quit]" ">> What climate do you want to edit?" Tell Tell DoReadLine strip QCheck ".list" over stringpfx if DoListClimates pop continue then DoCapAll ourClimate ! "_temp/%me/" me @ intostr "%me" subst ourDir ! break repeat begin ">> EDIT OPTIONS FOR %name" ourClimate @ "%name" subst toupper Tell " " Tell " " " D) Extreme Winter Low H) Climate Name" " C) Normal Winter Low G) Changeability Scale" " B) Extreme Summer High F) Rain Scale" " A) Normal Summer High E) Normal Day/Night Swing" Tell Tell Tell Tell Tell ">> Enter parameter, .s to show current values, .d when " "done, or .q to quit." strcat Tell DoReadLine strip QCheck ".done" over stringpfx if prog "_temp/%me" me @ intostr "%me" subst remove_prop pop exit then ".show" over stringpfx if ourClimate @ DoShowClimateVals pop continue then "A" over smatch if DoSetNSHT DoApplyTempVals ">> Normal summer high temperature set." Tell pop continue then "B" over smatch if DoSetESHT DoApplyTempVals ">> Extreme summer high temperature set." Tell pop continue then "C" over smatch if DoSetNWLT DoApplyTempVals ">> Normal winter low temperature set." Tell pop continue then "D" over smatch if DoSetEWLT DoApplyTempVals ">> Extreme winter low temperature set." Tell pop continue then "E" over smatch if DoSetNDND DoApplyTempVals ">> Normal day/night swing set." Tell pop continue then "F" over smatch if DoSetRain DoApplyTempVals ">> Rain scale set." Tell pop continue then "G" over smatch if DoSetChng DoApplyTempVals ">> Changeability scale set." Tell pop continue then "H" over smatch if begin ">> [Enter new name, or .q to quit]" ">> What do you want to change the name to?" Tell Tell DoReadLine strip QCheck prog "climates/%name/" 3 pick "%name" subst nextprop if ">> There is already a climate named %name." over "%name" subst Tell ">> Do you want to overwrite it? (y/n)" Tell DoReadYesNo not if ">> Aborted." Tell pop continue then then DoCapAll prog "climates/%old" ourClimate @ "%old" subst prog "climates/%new" 5 pick "%new" subst CopyDir prog "climates/%old" ourClimate @ "%old" subst remove_prop ourClimate ! ">> Climate renamed." Tell break repeat pop continue then ">> Sorry, invalid option." Tell pop repeat ; : DoDelClimate ( -- ) (* delete an existing climate *) begin ">> [Enter climate name, or .l to list choices, or .q to quit]" ">> What climate do you want to delete?" Tell Tell DoReadLine strip QCheck ".list" over stringpfx if DoListClimates pop continue then prog "climates/%name/" 3 pick "%name" subst nextprop not if ">> Climate '%name' not found." swap "%name" subst Tell continue then prog "climates/" rot strcat remove_prop ">> Climate deleted." Tell (* see if user maybe just deleted default climate; notify if so *) prog "climates/%clim/" prog "config/default" getpropstr "%clim" subst nextprop not if ">> WARNING: Default climate is no longer valid." Tell ">> You should set a new default climate." Tell then break repeat ; : DoSetDefault ( -- ) (* set default climate *) begin ">> Which climate should be the default?" Tell ">> [Enter default climate name, .l to list choices, " "or .q to quit]" strcat Tell DoReadLine strip QCheck ".list" over stringpfx if DoListClimates pop continue then prog "climates/%name/" 3 pick "%name" subst nextprop not if ">> Sorry, climate '%name' not found." swap "%name" subst Tell continue then prog "config/default" rot DoCapAll setprop ">> Default climate set." Tell break repeat ; : DoSetInterval ( -- ) (* set interval for weather updates *) begin ">> How often should the weather be updated?" Tell ">> [Enter a time string, such as '1 hour' or '90 minutes', or .q " "to quit." strcat Tell DoReadLine strip QCheck DoParseTimeString not if ">> Invalid entry." Tell continue then prog "config/interval" rot intostr setprop ">> Interval set." Tell break repeat ; : DoConfigure ( -- ) (* configure weather system *) me @ "W" flag? if begin ">> WEATHER CONFIGURATION:" Tell " " Tell " A) List climates D) Delete a climate" Tell " B) Add a climate E) Set default climate" Tell " C) Edit a climate F) Set interval" Tell " " Tell ">> Enter option A-F, or .q to quit." Tell DoReadLine strip QCheck "A" over smatch if DoListClimates else "B" over smatch if DoAddClimate else "C" over smatch if DoEditClimate else "D" over smatch if DoDelClimate else "E" over smatch if DoSetDefault else "F" over smatch if DoSetInterval else ">> Sorry, invalid option." then then then then then then pop repeat else "Permission denied." Tell then ; : DoFullReport ( -- ) (* display a verbose weather report *) "------------------------------------------------------------------------------" Tell ">> Current Weather Conditions:" Tell " Temperature ............... %val degrees" DoGetTemperature "%val" subst Tell " Precipitation ............. %val" DoGetPrecipNoun dup not if pop "none" then "%val" subst Tell " Cloud cover ............... %val" DoGetSkyAdj "%val" subst Tell " Wind ...................... %val1, at %val2" DoGetWindDirAdj "%val1" subst DoGetWindSpeed "%val2" subst Tell " Moon ...................... %val1 %val2" DoGetWaxWane "%val1" subst DoGetMoonPhase "%val2" subst Tell "------------------------------------------------------------------------------" Tell ; : DoReport ( -- ) (* display basic weather conditions for loc *) ">> The temperature is %temp." DoGetTempAdj "%temp" subst DoGetPrecipVerb dup if ", and it's " swap strcat "." strcat "." subst else pop then Tell ; : main DoChecks dup if dup "#*" smatch if dup " " instr if dup " " instr strcut strip ourArg ! strip ourOpt ! else strip ourOpt ! then then then ourOpt @ if ourOpt @ "#mpi" stringpfx if "#mpiclimate" ourOpt @ smatch if DoGetClimate else "#mpidaynight" ourOpt @ smatch if DoGetDayNight else "#mpidegrees" ourOpt @ smatch if DoGetTemperature else "#mpimoonphase" ourOpt @ smatch if DoGetMoonPhase else "#mpiphase" ourOpt @ smatch if DoGetDayPhase else "#mpiprecip" ourOpt @ smatch if DoGetPrecipNoun else "#mpiprecipverb" ourOpt @ smatch if DoGetPrecipVerb else "#mpitemp" ourOpt @ smatch if DoGetTempAdj else "#mpiseason" ourOpt @ smatch if DoGetSeason else "#mpiskyadj" ourOpt @ smatch if DoGetSkyAdj else "#mpiskynoun" ourOpt @ smatch if DoGetSkyNoun else "#mpiwaxwane" ourOpt @ smatch if DoGetWaxWane else "#mpiwindadj" ourOpt @ smatch if DoGetWindAdj else "#mpiwinddir" ourOpt @ smatch if DoGetWindDir else "#mpiwinddiradj" ourOpt @ smatch if DoGetWindDirAdj else "#mpiwindnoun" ourOpt @ smatch if DoGetWindNoun else "#mpiwindspeed" ourOpt @ smatch if DoGetWindSpeed else "" then then then then then then then then then then then then then then then then then exit else "#help" ourOpt @ stringpfx if DoHelp else "#full" ourOpt @ stringpfx if DoFullReport else "#climate" ourOpt @ stringpfx if DoClimate else "#start" ourOpt @ stringpfx if DoStartWeather else "#stop" ourOpt @ stringpfx if DoStopWeather else "#yes" ourOpt @ stringpfx if DoSetYes else "#no" ourOpt @ stringpfx if DoSetNo else "#configure" ourOpt @ stringpfx if DoConfigure else ">> #Option not found." Tell then then then then then then then then then else DoReport then ; . c q . c q