This is a daemon for landmarks which function as points-of-interest and possess data which can be used to add fake-items to a room.
First, the support files that are needed:
/* ** HEADER for /secure/sefun/boxdrawing.c **
* 2005-Jul-31 - T. Cook
* boxdrawing character handling
* -----
* 2011-Aug-16 T. Cook revised for DeadSouls
* 2011-Jul-17 T. Cook began altering for DeadSouls
* 2010-May-21 T. Cook reformatted for notepad++
* 2009-Dec-30 T. Cook modified for tublib, streamlined
* 2005-Sep-27 T. Cook tidied up
*/
#pragma strong_types
#ifndef __boxdrawing__
#define __boxdrawing__
/* info for box()
func: get boxdrawing character
args: int form (UDRL 1=single, 2=double, 0=none)
string style "OEM", "UTF-8", "Latin-1" [default]
rtrn: string boxdrawing character
*/
string box( int form, string style ){
switch( style ){
case "OEM":
switch( form ){
case 0000: return "\x20";
case 0011: return "\xC4";
case 0022: return "\xCD";
case 0101: return "\xBF";
case 0102: return "\xB8";
case 0110: return "\xDA";
case 0111: return "\xC2";
case 0120: return "\xD5";
case 0122: return "\xD1";
case 0201: return "\xB7";
case 0202: return "\xBB";
case 0210: return "\xD6";
case 0211: return "\xD2";
case 0220: return "\xC9";
case 0222: return "\xCB";
case 1001: return "\xD9";
case 1002: return "\xBE";
case 1010: return "\xC0";
case 1011: return "\xC1";
case 1020: return "\xD4";
case 1022: return "\xCF";
case 1100: return "\xB3";
case 1101: return "\xB4";
case 1102: return "\xB5";
case 1110: return "\xC3";
case 1111: return "\xC5";
case 1120: return "\xC6";
case 1122: return "\xD8";
case 2001: return "\xBD";
case 2002: return "\xBC";
case 2010: return "\xD3";
case 2011: return "\xD0";
case 2020: return "\xC8";
case 2022: return "\xCA";
case 2200: return "\xBA";
case 2201: return "\xB6";
case 2202: return "\xB9";
case 2210: return "\xC7";
case 2211: return "\xD7";
case 2220: return "\xCC";
case 2222: return "\xCE";
default: return "\x3F";
}
break;
case "UTF-8":
switch( form ){
case 0000: return "\xE3\x80\x80";
case 0011: return "\xE2\x94\x80";
case 0022: return "\xE2\x95\x90";
case 0101: return "\xE2\x94\x90";
case 0102: return "\xE2\x95\x95";
case 0110: return "\xE2\x94\x8C";
case 0111: return "\xE2\x94\xAC";
case 0120: return "\xE2\x95\x92";
case 0122: return "\xE2\x95\xA4";
case 0201: return "\xE2\x95\x96";
case 0202: return "\xE2\x95\x97";
case 0210: return "\xE2\x95\x93";
case 0211: return "\xE2\x95\xA5";
case 0220: return "\xE2\x95\x94";
case 0222: return "\xE2\x95\xA6";
case 1001: return "\xE2\x94\x98";
case 1002: return "\xE2\x95\x9B";
case 1010: return "\xE2\x94\x94";
case 1011: return "\xE2\x94\xB4";
case 1020: return "\xE2\x95\x98";
case 1022: return "\xE2\x95\xA7";
case 1100: return "\xE2\x94\x82";
case 1101: return "\xE2\x94\xA4";
case 1102: return "\xE2\x95\xA1";
case 1110: return "\xE2\x94\x9C";
case 1111: return "\xE2\x94\xBC";
case 1120: return "\xE2\x95\x9E";
case 1122: return "\xE2\x95\xAA";
case 2001: return "\xE2\x95\x9C";
case 2002: return "\xE2\x95\x9D";
case 2010: return "\xE2\x95\x99";
case 2011: return "\xE2\x95\xA8";
case 2020: return "\xE2\x95\x9A";
case 2022: return "\xE2\x95\xA9";
case 2200: return "\xE2\x95\x91";
case 2201: return "\xE2\x95\xA2";
case 2202: return "\xE2\x95\xA3";
case 2210: return "\xE2\x95\x9F";
case 2211: return "\xE2\x95\xAB";
case 2220: return "\xE2\x95\xA0";
case 2222: return "\xE2\x95\xAC";
default: return "\xEF\xBC\x9F";
}
break;
default:
switch( form ){
case 0000: return " ";
case 0011: return "-";
case 0022: return "=";
case 0101: return "+";
case 0102: return "+";
case 0110: return "+";
case 0111: return "+";
case 0120: return "+";
case 0122: return "+";
case 0201: return "+";
case 0202: return "+";
case 0210: return "+";
case 0211: return "+";
case 0220: return "+";
case 0222: return "+";
case 1001: return "+";
case 1002: return "+";
case 1010: return "+";
case 1011: return "+";
case 1020: return "+";
case 1022: return "+";
case 1100: return "|";
case 1101: return "+";
case 1102: return "+";
case 1110: return "+";
case 1111: return "+";
case 1120: return "+";
case 1122: return "+";
case 2001: return "+";
case 2002: return "+";
case 2010: return "+";
case 2011: return "+";
case 2020: return "+";
case 2022: return "+";
case 2200: return "|";
case 2201: return "+";
case 2202: return "+";
case 2210: return "+";
case 2211: return "+";
case 2220: return "+";
case 2222: return "+";
default: return "?";
}
break;
}
return "";
}
#endif
/* EOF */
/* /secure/sefun/extra_format.c **
* 2012-Jan-31 T. Cook
* extra text-formatting macros
*/
#pragma strong_types
#ifndef __extraformat__
#define __extraformat__
string HelpInfo( mapping M_args ){
string s_text = "Syntax: ", s_format_syntax = "'%%^B_WHITE%%^%%^BLACK%%^%s%%^RESET%%^'";
mixed m_arg;
int i_flag, i_count, i_max;
if( !mapp( M_args ) ){ return "Invalid data was passed to HelpInfo()."; }
if( member_array( "syntax", keys( M_args ) ) == -1 ){
s_text += "%^RED%^%^FLASH%^unknown%^RESET%^";
}else{
switch( typeof( M_args["syntax"] ) ){
case "array":
foreach( m_arg in M_args["syntax"] ){
if( !stringp( m_arg ) ){ continue; }
if( i_flag == 0 ){
i_flag = 1;
}else{
s_text += " ";
}
s_text += sprintf( s_format_syntax, strip_colours( m_arg ) ) + "\n";
}
break;
case "string":
s_text += sprintf( s_format_syntax, strip_colours( M_args["syntax"] ) ) + "\n";
break;
default:
s_text += "%^RED%^%^FLASH%^unknown%^RESET%^";
break;
}
}
s_text += "\n";
if( member_array( "body", keys( M_args ) ) == -1 ){
s_text += "This help topic has no detailed information.";
}else{
if( !stringp( M_args["body"] ) ){
s_text += "This help topic has no detailed information.";
}else{
s_text += M_args["body"];
}
}
s_text += "\n";
if( member_array( "synonyms", keys( M_args ) ) > -1 ){
switch( typeof( M_args["synonyms"] ) ){
case "array":
s_text += "\nSynonyms: ";
foreach( m_arg in M_args["synonyms"] ){
if( typeof( m_arg ) != "string" ){ M_args["synonyms"] -= ({ m_arg }); }
}
s_text += implode( M_args["synonyms"], ", " ) + "\n";
break;
case "string":
s_text += "\nSynonyms: " + M_args["synonyms"] + "\n";
break;
default:
break;
}
}
if( member_array( "see also", keys( M_args ) ) > -1 ){
switch( typeof( M_args["see also"] ) ){
case "array":
s_text += "\nSee also: ";
foreach( m_arg in M_args["see also"] ){
if( typeof( m_arg ) != "string" ){ M_args["see also"] -= ({ m_arg }); }
}
s_text += implode( M_args["see also"], ", " ) + "\n";
break;
case "string":
s_text += "\nSee also: " + M_args["see also"] + "\n";
break;
default:
break;
}
}
return s_text;
}
varargs string DebugFun( string s_fun, mixed ret, mixed *args... ){
string C = "%^CYAN%^", BR = "%^BOLD%^%^RED%^", BG = "%^BOLD%^%^GREEN%^", X = "%^RESET%^";
string *sa_A = ({}), s_element;
string s_A, s_B, space = repeat_string( " ", 20 );
int i, n, scr_x = this_player()->GetScreen()[0];
s_A = C;
if( !s_fun || !stringp( s_fun ) ){
s_A += "NO_FUNCTION";
}else{
s_A += s_fun;
}
s_A += BR + "( " + X;
if( arrayp( args ) && arrayp( args[0] ) && sizeof( args[0] ) > 1 ){
foreach( s_element in args[0] ){
sa_A += ({ sprintf( "%O", s_element ) });
}
}else if( arrayp( args ) && sizeof( args ) > 1 ){
foreach( s_element in args ){
sa_A += ({ sprintf( "%O", s_element ) });
}
}else{
sa_A += ({ args });
}
s_A += implode( sa_A, BR + ", " + X );
s_A += BR + " )" + X;
i = sizeof( strip_colours( s_A ) );
n = sizeof( strip_colours( sprintf( "%O", ret ) ) ) + 1;
if( ret ){
if( sizeof( sprintf( "%O", ret ) ) + 2 > scr_x - i ){
s_A += "\n" + sprintf( "%O", ret );
}else{
s_A += space[0..scr_x - i - n] + sprintf( "%s%O", ret == 0 ? BR : ret == 1 ? BG : "", ret ) + X;
}
}
return s_A;
}
#endif
/* EOF */
/* ** HEADER for /secure/sefuns/directions.c **
* 2005-Jun-28 - T. Cook
* convert horizontal coordinates to world directions
* -----
* 2012-Jun-10 T. Cook streamlined
* 2012-Jun-05 T. Cook changed to mappings, made sefun
* 2011-Jul-17 T. Cook began altering for DeadSouls
* 2010-May-31 T. Cook reformatted for notepad++
* 2009-Mar-15 T. Cook modified for tublib, tidied up
* 2005-Sep-27 T. Cook tidied up, renamed functions
*/
#pragma strong_types
#ifndef __directions__
#define __directions__
/* info for az_dir()
func: convert azimuth to compass direction
args: mixed azimuth in degrees
rtrn: string compass direction
*/
string az_dir( mixed m_az ){
float f_item;
string s_output = "thataway";
mapping M_dirs = ([
0.00: "north",
11.25: "north by northeast",
33.75: "northeast",
56.25: "east by northeast",
78.75: "east",
101.25: "east by southeast",
123.75: "southeast",
146.25: "south by southeast",
168.75: "south",
191.25: "south by southwest",
213.75: "southwest",
236.25: "west by southwest",
258.75: "west",
281.25: "west by northwest",
303.75: "northwest",
326.25: "north by northwest",
348.75: "north"
]);
foreach( f_item in sort_array( keys( M_dirs ), 1 ) ){
if( to_float( m_az ) >= f_item ){ s_output = M_dirs[f_item]; }
}
return s_output;
}
/* info for alt_dir()
func: convert altitude to direction
args: mixed altitude in degrees, int ground presence
rtrn: string direction
*/
varargs string alt_dir( mixed m_alt, int i_ground ){
mapping M_dirs = ([ ]);
float f_item;
string s_output = "in your mind";
if( i_ground ){
M_dirs = ([
-90.00: "below the horizon",
0.00: "on the horizon",
11.25: "low in the sky",
33.75: "halfway to zenith",
56.25: "near zenith",
78.75: "at zenith"
]);
}else{
M_dirs = ([
-90.00: "straight down",
-78.75: "far below eye level",
-56.25: "below eye level",
-33.75: "just below eye level",
-11.25: "at eye level",
11.25: "just above eye level",
33.75: "above eye level",
56.25: "far above eye level",
78.75: "straight up"
]);
}
foreach( f_item in sort_array( keys( M_dirs ), 1 ) ){
if( to_float( m_alt ) >= f_item ){ s_output = M_dirs[f_item]; }
}
return s_output;
}
#endif
/* EOF */
Here is the landmarks daemon itself:
/*
* /daemon/landmarks.c
* hold a list of landmarks that can be queried for direction/distance for description use
* 2011-Oct-21 T. Cook
* -----
* 2012-Jun-10 T. Cook added GetRelativePoint()
* T. Cook made relative distance based on horizon visible to player's GetHeight()
* 2012-Jun-09 T. Cook removed corners for landmark, made single-point position
* 2012-Jun-05 T. Cook made each sense have its own range
* 2012-Jun-04 T. Cook added ChangeLandmarkSense()
* 2012-May-31 T. Cook added better error messages to GetLandmarkPositions()
* changed ia_locus to fa_locus
* 2012-Jan-22 T. Cook simplified AddLandmark()
* added ChangeLandmark()
* 2011-Dec-03 T. Cook changed GetLandmarkLoci() to GetLandmarks()
* added ["itemdesc"] mapping
* added RemoveAll()
* 2011-Dec-01 T. Cook changed (size x, z) to bounding box corner
* changed (i_x, i_y, i_z) to ia_xyz
* changed to mapping for "corner 1", "corner 2", "midpoint" elements
* 2011-Nov-29 T. Cook added world property
* 2011-Oct-25 T. Cook changed (string s_size) to (int i_size_x, int i_size_z)
* 2011-Oct-24 T. Cook made RemoveLandmark() remove town from mapping if last landmark in that town removed
* added size property to AddLandmark()
* changed for() loops to foreach()
* 2011-Oct-23 T. Cook added GetLandmarkList()
* switched azimuth calculation to bearing() call
* Note: square 1.75x1.75 units @ 100 units distance is 1x1 degree, @ 50 is 2x2 degrees, @ 200 is 0.5x0.5 degrees
*/
#include <lib.h>
inherit LIB_DAEMON;
mapping GetLandmarks();
mapping GetLandmarkPositions( string s_world, string s_town, float *fa_locus );
string GetLandmarkList( mapping M_LandmarkPos );
mixed AddLandmark( string s_world, string s_town, string s_name );
varargs mixed ChangeLandmarkSense( string s_world, string s_town, string s_name, string s_desc, string s_msg, float f_dist );
mixed ChangeLandmark( string s_world, string s_town, string s_name, string s_property, mixed m_val );
mixed RemoveLandmark( string s_world, string s_town, string s_name );
mixed RemoveAll();
mapping M_Landmarks = ([ ]);
static string SaveFile;
static void create(){
daemon::create();
SaveFile = save_file( "/save/landmarks.o" );
if( unguarded( (: file_exists( SaveFile ) :) ) ){
RestoreObject( SaveFile, 1 );
}
SaveObject( SaveFile, 1 );
}
/* name: GetLandmarks
* args: none
* rtrn: mapping of current landmarks
*/
mapping GetLandmarks(){
return M_Landmarks;
}
/* name: GetLandmarkPositions
* args: string world, string town, coordinates( float x, float y, float z )
* rtrn: mapping [world] : ([
* [town] : ([
* [landmark name] : ([
* ["corner 1"] : ([ "azimuth", "altitude", "distance" ]),
* ["corner 2"] : ([ "azimuth", "altitude", "distance" ]),
* ["midpoint"] : ([ "azimuth", "altitude", "distance" ]),
* ["vision"] : "desc",
* ["smell"] : "desc",
* ["listen"] : "desc",
* ["touch"] : "desc",
* ["taste"] : "desc"
* ])
* ])
* ])
*/
mapping GetLandmarkPositions( string s_world, string s_town, float *fa_locus ){
mapping M_args = ([ ]);
string *sa_senses = ({ "vision", "smell", "listen", "touch", "taste" }), s_sense;
string s_landmark;
float f_range;
int i_count;
mapping M_LandmarkPosition = ([ ]);
// error-checking
if( !s_world || !s_town ){
return ([ ]);
}
if( !stringp( s_world ) ){
write( sprintf(
"Invalid, non-string world (%%^YELLOW%%^%s%%^RESET%%^) was passed to GetLandmarkPositions().",
identify( s_world )
) );
}
if( !stringp( s_town ) ){
write( sprintf(
"Invalid, non-string town (%%^YELLOW%%^%s%%^RESET%%^) was passed to GetLandmarkPositions().",
identify( s_town )
) );
}
if( !arrayp( fa_locus ) ){
write( sprintf(
"Invalid, non-array location (%%^YELLOW%^s%%^RESET%%^) was passed to GetLandmarkPositions().",
identify( fa_locus )
) );
return ([ ]);
}
if( sizeof( fa_locus ) != 3 ){
write( sprintf(
"Invalid location triplet (%%^YELLOW%%^%s%%^RESET%%^) was passed to GetLandmarkPositions().",
identify( fa_locus )[3..<4]
) );
return ([ ]);
}
for( i_count = 0; i_count < 3; ++i_count ){
if( !floatp( fa_locus[i_count] ) ){ fa_locus[i_count] = to_float( fa_locus[i_count] ); }
}
if(
sizeof( M_Landmarks ) == 0 ||
member_array( s_world, keys( M_Landmarks ) ) == -1 ||
member_array( s_town, keys( M_Landmarks[s_world] ) ) == -1 ||
sizeof( M_Landmarks[s_world][s_town] ) == 0
){
return ([ ]);
}
foreach( s_landmark in keys( M_Landmarks[s_world][s_town] ) ){
//----- initialise mappings -----
M_LandmarkPosition[s_world] = ([ ]);
M_LandmarkPosition[s_world][s_town] = ([ ]);
M_LandmarkPosition[s_world][s_town][s_landmark] = ([ ]);
M_LandmarkPosition[s_world][s_town][s_landmark]["position"] = ([ ]);
M_LandmarkPosition[s_world][s_town][s_landmark]["vision"] = ([ ]);
M_LandmarkPosition[s_world][s_town][s_landmark]["smell"] = ([ ]);
M_LandmarkPosition[s_world][s_town][s_landmark]["listen"] = ([ ]);
M_LandmarkPosition[s_world][s_town][s_landmark]["touch"] = ([ ]);
M_LandmarkPosition[s_world][s_town][s_landmark]["taste"] = ([ ]);
//----- maths for altitude/azimuth/distance -----
foreach( s_sense in sa_senses ){
if( M_Landmarks[s_world][s_town][s_landmark][s_sense]["dist"] ){
if( f_range < M_Landmarks[s_world][s_town][s_landmark][s_sense]["dist"] ){
f_range = M_Landmarks[s_world][s_town][s_landmark][s_sense]["dist"];
}
}
}
M_LandmarkPosition[s_world][s_town][s_landmark]["position"] = ([ ]);
M_args["position"] = ([ ]);
M_args["position"]["x"] = M_Landmarks[s_world][s_town][s_landmark]["position"]["x"];
M_args["position"]["y"] = M_Landmarks[s_world][s_town][s_landmark]["position"]["y"];
M_args["position"]["z"] = M_Landmarks[s_world][s_town][s_landmark]["position"]["z"];
M_args["position"]["dist locus to x"] = M_args["position"]["x"] - fa_locus[0];
M_args["position"]["dist locus to y"] = M_args["position"]["y"] - fa_locus[1];
M_args["position"]["dist locus to z"] = M_args["position"]["z"] - fa_locus[2];
M_args["position"]["dist locus to xy"] = sqrt(
M_args["position"]["dist locus to x"] * M_args["position"]["dist locus to x"] +
M_args["position"]["dist locus to y"] * M_args["position"]["dist locus to y"]
);
M_args["position"]["dist locus to xyz"] = sqrt(
M_args["position"]["dist locus to x"] * M_args["position"]["dist locus to x"] +
M_args["position"]["dist locus to y"] * M_args["position"]["dist locus to y"] +
M_args["position"]["dist locus to z"] * M_args["position"]["dist locus to z"]
);
// calculate altitude/azimuth if the landmark is in range
if( M_args["position"]["dist locus to xyz"] < f_range ){
M_LandmarkPosition[s_world][s_town][s_landmark]["position"]["azimuth"] =
bearing( fa_locus[0], fa_locus[1], M_args["position"]["x"], M_args["position"]["y"] );
if( M_args["position"]["dist locus to xy"] != 0.0 ){
M_LandmarkPosition[s_world][s_town][s_landmark]["position"]["altitude"] =
M_args["position"]["dist locus to z"] == 0.0 ? 0.0 : 360.0 * atan( (
M_args["position"]["dist locus to xyz"] - M_args["position"]["dist locus to xy"] ) /
M_args["position"]["dist locus to z"] ) / 3.1415926535;
}else{
M_LandmarkPosition[s_world][s_town][s_landmark]["position"]["altitude"] =
M_args["position"]["dist locus to z"] > 0.0 ? 90.0 : M_args["position"]["dist locus to z"] ?
0.0 : -90.0;
}
}
M_LandmarkPosition[s_world][s_town][s_landmark]["position"]["distance"] =
M_args["position"]["dist locus to xyz"];
//----- assign available sense descriptions that are within range ---
foreach( s_sense in sa_senses ){
if(
member_array( s_sense, keys( M_Landmarks[s_world][s_town][s_landmark] ) ) > -1 &&
M_Landmarks[s_world][s_town][s_landmark][s_sense] != "" &&
M_Landmarks[s_world][s_town][s_landmark][s_sense]["dist"] <= f_range
){
M_LandmarkPosition[s_world][s_town][s_landmark][s_sense] =
M_Landmarks[s_world][s_town][s_landmark][s_sense]["desc"];
}
}
//----- if the landmark doesn't actually have any sense descriptions, remove it from the list
f_range = M_LandmarkPosition[s_world][s_town][s_landmark]["position"]["dist locus to xyz"];
foreach( s_sense in keys( M_LandmarkPosition[s_world][s_town][s_landmark] ) ){
if( undefinedp( M_LandmarkPosition[s_world][s_town][s_landmark][s_sense] ) ){
map_delete( M_LandmarkPosition[s_world][s_town][s_landmark], s_sense );
}
}
if( sizeof( keys( M_LandmarkPosition[s_world][s_town][s_landmark] ) ) == 0 ){
map_delete( M_LandmarkPosition[s_world][s_town], s_landmark );
}
}
return M_LandmarkPosition[s_world][s_town];
}
/* name: GetRelativePoint
* args: float azimuth, float altitude, float distance in metres
* rtrn: string relative location of point
*/
string GetRelativePoint( float f_az, float f_alt, float f_dist, int i_ground ){
string s_elev = "", s_direction = "", s_distance = "";
float f_item;
mapping M_distances = ([
-1.0: " right here",
0.0: " within arm's reach",
0.8: " a pace away",
1.0: " very close by",
5.0: " close by",
10.0: " nearby",
20.0: " a stone's throw",
22.0: " a short distance",
100.0: " a ways",
840.0: " a ten minutes' walk",
880.0: " a good way",
1600.0: " a mile away",
1609.0: " in the distance",
2500.0: " a half-hour's walk",
2550.0: " a long way",
3200.0: " a very long way",
4000.0: " far away",
4750.0: " beyond the horizon"
]);
s_elev = alt_dir( f_alt, i_ground );
s_direction = az_dir( f_az );
foreach( f_item in sort_array( keys( M_distances ), 1 ) ){
if( f_dist >= f_item ){ s_distance = M_distances[f_item]; }
}
return s_elev +
( s_elev == "" || s_direction == "" ? "" : "," ) +
( s_elev != "" && s_direction == "" ? " and" : "" ) +
s_distance +
( s_direction == "" ? "" : " to the " + s_direction );
}
/* name: GetLandmarkList
* args: mapping([ "landmark" : ([ "azimuth" : azimuth, "altitude" : altitude, "distance" : distance ]) ])
* rtrn: string list of landmarks from mapping within given distance
*/
string GetLandmarkList( mapping M_LandmarkPos ){
string *sa_landmarks = ({}), *sa_keys = ({});
string *sa_senses = ({ "vision", "smell", "listen", "touch", "taste" }), s_sense;
string *sa_wworp = ({});
string s_landmark = "";
if( sizeof( M_LandmarkPos ) == 0 ){ return "There are no significant landmarks here."; }
foreach( s_landmark in keys( M_LandmarkPos ) ){
sa_keys = keys( M_LandmarkPos[s_landmark]["position"] );
if(
member_array( "azimuth", sa_keys ) == -1 ||
member_array( "altitude", sa_keys ) == -1 ||
member_array( "distance", sa_keys ) == -1
){
continue;
}
foreach( s_sense in sa_senses ){
if( !undefinedp( M_LandmarkPos[s_landmark][s_sense] ) ){
switch( s_sense ){
case "vision": sa_wworp += ({ "seen" }); break;
case "smell": sa_wworp += ({ "smelled" }); break;
case "listen": sa_wworp += ({ "heard" }); break;
case "touch": sa_wworp += ({ "felt" }); break;
case "taste": sa_wworp += ({ "tasted" }); break;
default: sa_wworp += ({ "sensed" }); break;
}
}
}
if( s_landmark == "" ){ s_landmark = "something"; }
sa_landmarks += ({
( s_landmark == "" ? "something" : GetArticle( s_landmark ) + " " + s_landmark ) + " which can be " +
item_list( sa_wworp ) + " " +
GetRelativePoint(
M_LandmarkPos[s_landmark]["position"]["azimuth"],
M_LandmarkPos[s_landmark]["position"]["altitude"],
M_LandmarkPos[s_landmark]["position"]["distance"],
1
)
});
}
return sizeof( sa_landmarks ) == 0 ? "" : "There " +
( sizeof( sa_landmarks ) == 1 ? "is " : "are " ) + item_list( sa_landmarks ) + ".";
}
/* name: AddLandmark
* args: string name, corner 1 locus( int x, int y, int z ), corner 2 locus( int x, int y, int z ), mapping descs
* rtrn: set mapping value for landmark of given size centred at given coordinates if nonexistant
*/
mixed AddLandmark( string s_world, string s_town, string s_name ){
if( !stringp( s_world ) ){ write( "Invalid world passed to AddLandmark()." ); return 0; }
if( !stringp( s_town ) ){ write( "Invalid town passed to AddLandmark()." ); return 0; }
if( !stringp( s_name ) ){ write( "Invalid landmark name passed to AddLandmark()." ); return 0; }
if( member_array( s_world, keys( M_Landmarks ) ) == -1 ){ M_Landmarks[s_world] = ([ ]); }
if( member_array( s_town, keys( M_Landmarks[s_world] ) ) == -1 ){
M_Landmarks[s_world][s_town] = ([ ]);
}
if( member_array( s_name, keys( M_Landmarks[s_world][s_town] ) ) > -1 ){
return "The landmark '" + s_name + "' in the town '" + s_town + "' on the world '" +
s_world + "' already exists!";
}else{
M_Landmarks[s_world][s_town][s_name] = ([
"position": ([ "x": 0.0, "y": 0.0, "z": 0.0 ]),
"vision": ([ "dist": 0.0 ]),
"smell": ([ "dist": 0.0 ]),
"listen": ([ "dist": 0.0 ]),
"touch": ([ "dist": 0.0 ]),
"taste": ([ "dist": 0.0 ]),
]);
}
SaveObject( SaveFile, 1 );
return 1;
}
/* name: ChangeLandmarkSense
* args: string world, town, name, sense, description, notification message, float distance for sense description
* rtrn: int success state
*/
varargs mixed ChangeLandmarkSense(
string s_world, string s_town, string s_name, string s_sense, string s_desc, string s_msg, float f_dist
){
string *sa_senses = ({ "vision", "smell", "listen", "touch", "taste" }), s_what;
object *oa_viewers = ({});
mapping M_tmp;
if( !s_world || !stringp( s_world ) || member_array( s_world, keys( M_Landmarks ) ) == -1 ){
write( "Invalid world passed to ChangeLandmarkSense()." ); return 0;
}
if( !s_town || !stringp( s_town ) || member_array( s_town, keys( M_Landmarks[s_world] ) ) == -1 ){
write( "Invalid town passed to ChangeLandmarkSense()." ); return 0;
}
if( !s_name || !stringp( s_name ) || member_array( s_name, keys( M_Landmarks[s_world][s_town] ) ) == -1 ){
write( "Invalid landmark name passed to ChangeLandmarkSense()." ); return 0;
}
if( !s_sense || !stringp( s_sense ) || member_array( lower_case( s_sense ), sa_senses ) == -1 ){
write( "Invalid sense passed to ChangeLandmarkSense()." ); return 0;
}
if( !f_dist ){
f_dist = 0.0;
}
oa_viewers = filter(
users(),
(: member_array( $(s_name), keys( GetLandmarkPositions( $(s_world), $(s_town), ({
to_float( environment( $1 )->GetProperty( "latitude" ) ),
to_float( environment( $1 )->GetProperty( "longitude" ) ),
to_float( environment( $1 )->GetProperty( "altitude" ) )
}) ) ) ) != -1 :)
);
//----- sense removed from landmark -----
if( !s_desc || !stringp( s_desc ) || s_desc == "" || s_desc == "\"\"" ){
map_delete( M_Landmarks[s_world][s_town][s_name], s_sense );
switch( s_sense ){
case "vision": s_what = "visible"; break;
case "smell": s_what = "odorous"; break;
case "listen": s_what = "audible"; break;
case "touch": s_what = "tangible"; break;
case "taste": s_what = "tastable"; break;
default: s_what = "perceivable"; break;
}
message(
"landmark",
s_msg ? s_msg : capitalize( s_name ) + " is no longer " + s_what + ".",
oa_viewers
);
//---- new sense added to landmark -----
}else if( !M_Landmarks[s_world][s_town][s_name][s_sense] ){
M_Landmarks[s_world][s_town][s_name][s_sense]["desc"] = s_desc;
M_Landmarks[s_world][s_town][s_name][s_sense]["dist"] = f_dist;
switch( s_sense ){
case "vision": s_what = "seen"; break;
case "smell": s_what = "smelled"; break;
case "listen": s_what = "heard"; break;
case "touch": s_what = "felt"; break;
case "taste": s_what = "tasted"; break;
default: s_what = "perceived"; break;
}
message(
"landmark",
s_msg ? s_msg : capitalize( s_name ) + " can now be " + s_what + ".",
oa_viewers
);
//----- existing sense for landmark has changed -----
}else{
M_Landmarks[s_world][s_town][s_name][s_sense]["desc"] = s_desc;
M_Landmarks[s_world][s_town][s_name][s_sense]["dist"] = f_dist;
switch( s_sense ){
case "vision": s_what = "appearance"; break;
case "smell": s_what = "smell"; break;
case "listen": s_what = "sound"; break;
case "touch": s_what = "texture"; break;
case "taste": s_what = "taste"; break;
default: s_what = "somehow"; break;
}
message(
"landmark",
s_msg ? s_msg : capitalize( s_name ) + " changes " + s_what + ".",
oa_viewers
);
}
return 1;
}
/* name: ChangeLandmark
* args: string world, town, name, property, mixed value
* rtrn: int success state
*/
mixed ChangeLandmark( string s_world, string s_town, string s_name, string s_property, mixed m_val ){
string *sa_senses = ({ "vision", "smell", "listen", "touch", "taste" }), s_sense, s_what, s_val;
object *oa_viewers = ({});
float f_dist;
mapping M_tmp;
if( member_array( s_world, keys( M_Landmarks ) ) == -1 ){
write( "Invalid world passed to ChangeLandmark()." ); return 0;
}
if( member_array( s_town, keys( M_Landmarks[s_world] ) ) == -1 ){
write( "Invalid town passed to ChangeLandmark()." ); return 0;
}
if( member_array( s_name, keys( M_Landmarks[s_world][s_town] ) ) == -1 ){
write( "Invalid landmark name passed to ChangeLandmark()." ); return 0;
}
switch( s_property ){
case "position":
if( !mapp( m_val ) ){
write( "Values for modifying landmark location must be in mapping form." );
return 0;
}
if( undefinedp( m_val["x"] ) || undefinedp( m_val["y"] ) || undefinedp( m_val["z"] ) ){
write( "Mapping for modifying landmark corner requires \"x\", \"y\", and \"z\" values." );
return 0;
}
M_Landmarks[s_world][s_town][s_name]["position"] = ([
"x": to_float( m_val["x"] ), "y": to_float( m_val["y"] ), "z": to_float( m_val["z"] )
]);
SaveObject( SaveFile, 1 );
write( "Changed " + s_property + " for " + s_name + "." );
return 1;
case "vision": case "smell": case "listen": case "touch": case "taste":
if( !stringp( m_val ) ){
write( "Values for modifying landmark descriptions must be in string form." );
return 0;
}
if( sscanf( m_val, "%f, %s", f_dist, s_val ) == 2 ){
ChangeLandmarkSense( s_world, s_town, s_name, s_property, s_val, "", f_dist );
}else{
write( "Sense for landmark needs distance and string. '<DISTANCE>, <DESC>'" );
return 0;
}
SaveObject( SaveFile, 1 );
message(
"landmark",
( m_val == "" ? "Remov": "Chang" ) + "ed " + s_property + " for " + s_name + ".",
this_player()
);
return 1;
default:
if( member_array( s_property, keys( M_Landmarks[s_world][s_town][s_name] ) ) == -1 ){
write( "That property doesn't exist for landmarks." );
return 0;
}
M_Landmarks[s_world][s_town][s_name][s_property] = m_val;
SaveObject( SaveFile, 1 );
write( "Changed " + s_property + " for " + s_name + "." );
return 1;
}
}
/* name: RemoveLandmark
* args: string name
* rtrn: remove landmark from mapping if it exists
*/
mixed RemoveLandmark( string s_world, string s_town, string s_name ){
if(
member_array( s_world, keys( M_Landmarks ) ) == -1 &&
member_array( s_town, keys( M_Landmarks[s_world] ) ) == -1 &&
member_array( s_name, keys( M_Landmarks[s_world][s_town] ) ) == -1
){
return "The landmark '" + s_name + "' in the town '" + s_town + "' on the world '" +
s_world + "' didn't even exist!";
}
map_delete( M_Landmarks[s_world][s_town], s_name );
if( sizeof( M_Landmarks[s_world][s_town] ) == 0 ){
map_delete( M_Landmarks[s_world], s_town );
}
if( sizeof( M_Landmarks[s_world] ) == 0 ){
map_delete( M_Landmarks, s_world );
}
SaveObject( SaveFile, 1 );
return 1;
}
/* name: RemoveAll
* args: [none]
* rtrn: remove all landmarks from mapping
*/
mixed RemoveAll(){
M_Landmarks = ([ ]);
SaveObject( SaveFile, 1 );
return 1;
}
/* EOF */
Here is the admin command for adding, removing, listing, and modifying landmarks:
/* /secure/cmds/admins/landmark.c
* manage landmarks
* 2011-Oct-22 T. Cook
* -----
* 2012-Jun-09 T. Cook removed corners from landmark, made single-point position
* 2012-Jun-05 T. Cook made each sense have its own range
* 2012-Jun-04 T. Cook separated detailed info from listing of all landmarks
* 2012-Jun-01 T. Cook improved syntax, typo fixed
* 2012-Jan-22 T. Cook simplified "add landmark"
* added "change landmark"
* 2011-Dec-03 T. Cook changed GetLandmarkLoci() to GetLandmarks()
* added ["itemdesc"]
* added 'remove all'
* 2011-Dec-01 T. Cook changed (size x, z) to bounding box corner
* changed (i_x, i_y, i_z) to ia_xyz
* 2011-Nov-29 T. Cook added world property
* 2011-Oct-25 T. Cook changed (string s_size) to (int i_size_x, int i_size_z)
* 2011-Oct-24 T. Cook improved handling of add/remove, added size property
* changed for() loops to foreach()
* Note: square 1.75x1.75 units @ 100 units distance is 1x1 degree, @ 50 is 02x2 degrees, @200 is 0.5x0.5 degrees
*/
#include <lib.h>
#include <daemons.h>
inherit LIB_DAEMON;
string GetHelp( string s_str );
int RemoveLandmarks( string s_arg );
mixed cmd( string s_args ){
string s_World, s_Town, s_Name, s_opts, s_pair, s_enc, s_prop, s_val, *sa_keyval;
string *sa_senses = ({ "vision", "smell", "listen", "touch", "taste" }), s_sense;
string *sa_props = sa_senses + ({ "position" });
string *sa_worlds = ({}), *sa_towns = ({}), *sa_names = ({});
string s_color;
string s_blah = "";
int i_x, i_y, i_z, i_flag;
float f_x, f_y, f_z;
mixed m_val;
mapping M_Landmarks = LANDMARKS_D->GetLandmarks();
mapping M_c = ([
"BGW": "%^B_WHITE%^%^BLACK%^",
"X": "%^RESET%^",
"Y": "%^YELLOW%^",
"G": "%^GREEN%^",
"C": "%^CYAN%^",
"R": "%^RED%^",
"BC": "%^BOLD%^%^CYAN%^",
"BG": "%^BOLD%^%^GREEN%^",
"BW": "%^BOLD%^%^WHITE%^",
"BR": "%^BOLD%^%^RED%^"
]);
foreach( s_World in keys( M_Landmarks ) ){
sa_worlds += ({ lower_case( s_World ) });
foreach( s_Town in keys( M_Landmarks[s_World] ) ){
sa_towns += ({ lower_case( s_Town ) });
foreach( s_Name in keys( M_Landmarks[s_World][s_Town] ) ){
sa_names += ({ lower_case( s_Name ) });
}
}
}
//----- LIST ALL LANDMARKS -----
if( !s_args || s_args == "" ){
if( sizeof( M_Landmarks ) == 0 ){
write( "There are no landmarks available." );
}else{
s_enc = this_player()->GetProperty( "Encoding" );
write( M_c["BC"] + "** LANDMARKS **" + M_c["X"] );
write( sprintf(
"%s%s%s : %s%s%s : %s%-20s%s <POSITION>",
M_c["Y"], "World", M_c["X"], M_c["C"], "Town", M_c["X"], M_c["BG"], "Landmark", M_c["X"]
) );
write( repeat_string( box( 0011, s_enc ), this_player()->GetScreen()[0] ) );
foreach( s_World in keys( M_Landmarks ) ){
foreach( s_Town in keys( M_Landmarks[s_World] ) ){
foreach( s_Name in keys( M_Landmarks[s_World][s_Town] ) ){
f_x = to_float( M_Landmarks[s_World][s_Town][s_Name]["position"]["x"] );
f_y = to_float( M_Landmarks[s_World][s_Town][s_Name]["position"]["y"] );
f_z = to_float( M_Landmarks[s_World][s_Town][s_Name]["position"]["z"] );
write( sprintf(
"%s%s%s : %s%s%s : %s%-20s%s <%3.1f, %3.1f, %3.1f>",
M_c["Y"], s_World, M_c["X"], M_c["C"], s_Town, M_c["X"], M_c["BG"], s_Name, M_c["X"],
f_x, f_y, f_z
) );
}
}
}
}
//----- ADD LANDMARK -----
}else if( sscanf( s_args, "add %s", s_opts ) == 1 ){
if( regexp( lower_case( s_opts ), "(^| |:)(add|change|remove)($| |:)" ) == 1 ){
write( "The words 'add', 'change', or 'remove' can't be used as part of a world, town, or landmark name." );
return 1;
}
if( sscanf( s_opts, "%s:%s:%s", s_World, s_Town, s_Name ) == 3 ){
if(
member_array( lower_case( s_World ), sa_worlds ) > -1 &&
member_array( lower_case( s_Town ), sa_towns ) > -1 &&
member_array( lower_case( s_Name ), sa_names ) > -1
){
write( sprintf(
"The landmark '%s' already existed in the town '%s' on the world '%s'.", s_Name, s_Town, s_World
) );
return 1;
}
LANDMARKS_D->AddLandmark( s_World, s_Town, s_Name );
write( sprintf( "Landmark '%s' added to the town '%s' on the world '%s'.", s_Name, s_Town, s_World ) );
}else{
write( sprintf(
"\nSyntax: '%slandmark add <WORLD>:<TOWN>:<NAME>%s'\n\n" +
"Where <WORLD>, <TOWN>, and <NAME> are the names of the world, town, and landmark.\n",
M_c["BGW"], M_c["X"]
) );
}
//----- CHANGE LANDMARK PROPERTY -----
}else if( sscanf( s_args, "change %s", s_opts ) == 1 ){
if( sscanf( s_opts, "%s for %s:%s:%s to %s", s_prop, s_World, s_Town, s_Name, s_val ) == 5 ){
if( member_array( lower_case( s_prop ), sa_props ) == -1 ){
write( sprintf( "The property '%s' isn't valid for landmarks.", s_prop ) );
return 1;
}
if(
member_array( s_World, keys( M_Landmarks ) ) == -1 ||
member_array( s_Town, keys( M_Landmarks[s_World] ) ) == -1 ||
member_array( s_Name, keys( M_Landmarks[s_World][s_Town] ) ) == -1
){
write( sprintf(
"Couldn't find landmark '%s' in the town '%s' on the world '%s'. This command is case-sensitive.",
s_Name, s_Town, s_World
) );
return 1;
}
if(
regexp( lower_case( s_World ), "(^| |:)(add|change|remove)($| |:)" ) == 1 ||
regexp( lower_case( s_Town ), "(^| |:)(add|change|remove)($| |:)" ) == 1 ||
regexp( lower_case( s_Name ), "(^| |:)(add|change|remove)($| |:)" ) == 1
){
write(
"The words 'add', 'change', or 'remove' can't be used as part of a world, town, or landmark name."
);
return 1;
}
if( sscanf( s_val, "%f,%f,%f", f_x, f_y, f_z ) == 3 ){
m_val = ([ "x": f_x, "y": f_y, "z": f_z ]);
}else if( sscanf( s_val, "%d,%d,%d", i_x, i_y, i_z ) == 3 ){
m_val = ([ "x": to_float( i_x ), "y": to_float( i_y ), "z": to_float( i_z ) ]);
}else{
m_val = s_val;
}
LANDMARKS_D->ChangeLandmark( s_World, s_Town, s_Name, s_prop, m_val );
}else{
write( sprintf(
"\nSyntax: '%slandmark change <WHAT> for <WORLD>:<TOWN>:<NAME> to <VALUE>%s'\n\n" +
"Where <WORLD>, <TOWN>, and <NAME> are the names of the world, town and landmark, <WHAT> is the " +
"property to be changed (currently 'vision', 'smell', 'listen', 'touch', 'taste', 'corner 1', and " +
"'corner 2'), and <VALUE> is the property's new value (note: landmark descriptions cannot contain " +
"commas!).\n",
M_c["BGW"], M_c["X"]
) );
}
//----- REMOVE LANDMARK -----
}else if( sscanf( s_args, "remove %s", s_opts ) == 1 ){
if( regexp( lower_case( s_opts ), "(^| |:)(add|change|remove)($| |:)" ) == 1 ){
write( "The words 'add', 'change', or 'remove' can't be used as part of a world, town, or landmark name." );
return 1;
}
if( sscanf( s_opts, "%s:%s:%s", s_World, s_Town, s_Name ) == 3 ){
if(
member_array( s_World, keys( M_Landmarks ) ) != -1 &&
member_array( s_Town, keys( M_Landmarks[s_World] ) ) != -1 &&
member_array( s_Name, keys( M_Landmarks[s_World][s_Town] ) ) != -1
){
LANDMARKS_D->RemoveLandmark( s_World, s_Town, s_Name );
previous_object()->eventPrint( "Landmark removed." );
}else{
write( sprintf(
"Couldn't find the landmark '%s' in the town '%s' on the world '%s' to remove.",
s_Name, s_Town, s_World
) );
}
}else if( s_opts == "all" ){
write( "This will remove all landmarks! Are you certain you wish to do this (y/n)? " );
input_to( (: RemoveLandmarks :) );
}else{
write( sprintf(
"Syntax: '%slandmark remove <WORLD>,<TOWN>,<NAME>%s' or '%slandmark remove all%s'",
M_c["BGW"], M_c["X"], M_c["BGW"], M_c["X"]
) );
}
//----- LIST INDIVIDUAL LANDMARK -----
}else if( sscanf( s_args, "%s:%s:%s", s_World, s_Town, s_Name ) == 3 ){
if(
member_array( s_World, keys( M_Landmarks ) ) > -1 &&
member_array( s_Town, keys( M_Landmarks[s_World] ) ) > -1 &&
member_array( s_Name, keys( M_Landmarks[s_World][s_Town] ) ) > -1
){
write( sprintf(
"%s%s%s : %s%s%s : %s%-20s%s <%3.1f, %3.1f, %3.1f>",
M_c["Y"], s_World, M_c["X"], M_c["C"], s_Town, M_c["X"], M_c["BG"], s_Name, M_c["X"],
to_float( M_Landmarks[s_World][s_Town][s_Name]["position"]["x"] ),
to_float( M_Landmarks[s_World][s_Town][s_Name]["position"]["y"] ),
to_float( M_Landmarks[s_World][s_Town][s_Name]["position"]["z"] )
) );
foreach( s_sense in sa_senses ){
s_color = member_array(
s_sense, keys( M_Landmarks[s_World][s_Town][s_Name] )
) > -1 && !undefinedp( M_Landmarks[s_World][s_Town][s_Name][s_sense]["desc"] ) &&
M_Landmarks[s_World][s_Town][s_Name][s_sense]["desc"] != "" ? M_c["G"] : M_c["R"];
i_flag += member_array( s_sense, keys( M_Landmarks[s_World][s_Town][s_Name] ) ) > -1 ? 1 : 0;
s_blah += sprintf(
"\t%s%-6s%s (%4.2f): %s\n",
s_color, s_sense, M_c["X"],
!undefinedp( M_Landmarks[s_World][s_Town][s_Name][s_sense]["dist"] ) ?
to_float( M_Landmarks[s_World][s_Town][s_Name][s_sense]["dist"] ) : 0.0,
!undefinedp( M_Landmarks[s_World][s_Town][s_Name][s_sense]["desc"] ) ||
stringp( M_Landmarks[s_World][s_Town][s_Name][s_sense]["desc"] ) ?
M_Landmarks[s_World][s_Town][s_Name][s_sense]["desc"] : "No description."
);
}
write( i_flag ? s_blah[0..<2] : "\t" + M_c["BR"] + "Error:" + M_c["X"] + " No sense descriptions." );
}else{
write( sprintf(
"Couldn't find the landmark '%s' in the town '%s' on the world '%s'. This command is case-sensitive.",
s_Name, s_Town, s_World
) );
}
//----- LANDMARK HELP -----
}else{
return GetHelp( "" );
}
return 1;
}
int RemoveLandmarks( string s_arg ){
if( lower_case( s_arg )[0..0] == "y" ){
LANDMARKS_D->RemoveAll();
previous_object()->eventPrint( "All landmarks removed." );
}else{
previous_object()->eventPrint( "Landmarks unchanged." );
}
return 1;
}
string GetHelp( string s_str ){
return ( HelpInfo( ([
"syntax": ({
"landmark",
"landmark <NAME>",
"landmark add <WORLD>:<TOWN>:<NAME>",
"landmark remove <WORLD>:<TOWN>:<NAME>",
"landmark remove all",
"landmark change position for <WORLD>:<TOWN>:<NAME> to <X>,<Y>,<Z>",
"landmark change <SENSE>:desc for <WORLD>:<TOWN>:<NAME> to <DESCRIPTION>",
"landmark change <SENSE>:dist for <WORLD>:<TOWN>:<NAME> to <DESCRIPTION>"
}),
"body": "Landmarks are points of interest that the MUD can include in an outdoor room's description if a " +
"player is within a certain distance of it. The 'landmark' command allows management of landmark daemon " +
"elemnts. Using 'landmark' by itself will display a list of current landmarks, and 'landmark <NAME>' " +
"will show all properties of the specified landmark. <WORLD> is the world name, <TOWN> is the town name, " +
"<NAME> is the landmark name, <X>, <Y>, and <Z> are the floating-point coordinates of the center of the" +
"landmark, <DISTANCE> is a floating-point distance, <SENSE> is one of the sense vision, smell, listen, " +
"touch, or taste, and <DESCRIPTION> is a string. If \"\" is used for <DESCRIPTION>, that property will be " +
"removed from the landmark."
]) ) );
}
/* EOF */
IMPORTANT: Don't forget to...
add to /secure/include/daemons.h:
#define LANDMARKS_D DIR_DAEMONS "/landmarks"
add to /secure/sefun/sefun.h:
//===== boxdrawing.c
string box( int form, string style );
//===== extra_format.c
string HelpInfo( mapping M_args );
varargs string DebugFun( string s_fun, mixed ret, mixed *args... );
//===== directions.c
string az_dir( mixed az );
varargs string alt_dir( mixed f_alt, int i_ground );
add to /secure/sefun/sefun.c:
#include "/secure/sefun/extra_format.c"
#include "/secure/sefun/directions.c"
#include "/secure/sefun/boxdrawing.c"