/*    /lib/room/exits.c
 *    from the Dead Souls LPC Library
 *    handles players exiting from rooms
 *    created by Descartes of Borg 940711
 *    modified for new inheritance structure by Descartes 950208
 *    Version: @(#) exits.c 1.12@(#)
 *    Last Modified: 96/12/23
 */

#include <lib.h>
#include <rooms.h>
#include <position.h>
#include "include/exits.h"

private static string Obvious, GoMessage, EnterMessage, Dir, Sky;
private static mapping Exits, Doors;

static void create(){
    Exits = ([]);
    Doors = ([]);
    Obvious = "";
    Dir = "/" + implode(explode(file_name(), "/")[0..<2], "/");
    GoMessage = "You go nowhere at all.\n";
    EnterMessage = "You can't enter that!\n";
}

mixed CanGo(object who, string str){
    if( (int)who->GetParalyzed() ) return "You are unable to move.";
    if( !Exits[str] && str != "up" && str != "down" &&
      !(sizeof(this_object()->GetFlyRoom())) &&
      !(sizeof(this_object()->GetSinkRoom())) ) return GoMessage;
    else return 1;
}

mixed eventGo(object who, string str){
    if(query_verb() == "go" && interactive(this_player())){	
        if( who->GetPosition() != POSITION_STANDING ){  
            write("You are not standing.");
            return 0;
        }
    }
    else if(query_verb() == "crawl"){
        if( who->GetPosition() != POSITION_LYING &&
          who->GetPosition() != POSITION_KNEELING &&
          who->GetPosition() != POSITION_SITTING ){
            write("You are not in the correct position for crawling.");
            return 0;
        }
    }
    else if(query_verb() == "fly"){
        if( who->GetPosition() != POSITION_FLYING ){
            write("You are not flying.");
            return 0;
        }
    }
    else if(query_verb() == "swim"){
        if( who->GetPosition() != POSITION_SWIMMING ){
            write("You are not swimming.");
            return 0;
        }
    }

    if( sizeof(Doors) && Doors[str] && (int)Doors[str]->GetClosed() ){
        message("my_action", "You bump into " + 
          (string)Doors[str]->GetShort(str) + ".", who);
        return 1;
    }
    if( Exits[str] && Exits[str]["pre"] && 
      !((int)evaluate(Exits[str]["pre"], str)) )
        return 1;
    if(!Exits[str]){
        if( str == "up" && sizeof(this_object()->GetFlyRoom())){
            if(who->GetPosition() == POSITION_FLYING){
                string omsg = who->GetName()+" flies up.";
                string imsg = who->GetName()+" flies in.";
                who->eventMoveLiving(this_object()->GetFlyRoom(),omsg,imsg,str);
                return 1;
            }
        }

        if(str == "down" && sizeof(this_object()->GetSinkRoom())){
            string omsg = who->GetName()+" sinks down.";
            string imsg = who->GetName()+" sinks in.";
            who->eventMoveLiving(this_object()->GetSinkRoom(),omsg,imsg,str);
            return 1;
        }
        write("You can't go that way.");
        return 0;
    }
    who->eventMoveLiving(Exits[str]["room"],0,0,str);
    if( Exits[str]["post"] ) evaluate(Exits[str]["post"], str);
    return 1;
}

mixed GetDoor(string dir){
    if(sizeof(Doors)) return Doors[dir];
    else return 0;
}

string array GetDoors(){
    return keys(Doors);
}

string SetDoor(string dir, string file){
    object ob = GetDummyItem(dir);

    if( ob ){
        ob->SetDoor(file);
    }

    if(!file_exists(file) && !file_exists(file+".c")){
        return "Door not found.";
    }
    file->eventRegisterSide(dir);
    return (Doors[dir] = file); 
}

varargs string CreateDoor(string dir, string odir, string long, string locked, string key){
    object new_door = new(LIB_DOOR);
    string doorfile = file_name(new_door);
    object ob = GetDummyItem(dir);
    if(!locked) locked = "";
    if( ob ){
        ob->SetDoor(doorfile);
    }
    new_door->SetSide(dir, ([ "id" : ({"door", dir+" door", long}), "short" : "a door leading "+dir, "long" : long, "lockable" : 1 ]));
    new_door->SetSide(odir, ([ "id" : ({"door", odir+" door", long}), "short" : "a door leading "+odir, "long" : long, "lockable" : 1 ]));
    if(key) new_door->SetKeys(dir, ({ key }) );
    if(key) new_door->SetKeys(odir, ({ key }) );
    new_door->eventRegisterSide(dir);
    return (Doors[dir] = doorfile);
}

string GetDirection(string dest){
    foreach(string dir, mapping data in Exits){
        if( data["room"] == dest ){
            return "go " + dir;
        }
    }
    foreach(string dir in GetEnters()){
        string data = GetEnter(dir);

        if(data && data  == dest ){
            return "enter " + dir;
        }
    }
    return 0;
}

object GetDummyItem(mixed id){
    int i;
    object array dummies,all_inv;

    all_inv=all_inventory();
    dummies = ({});

    for(i=0; i<sizeof(all_inv); i++){
        if ( (mixed)all_inv[i]->isDummy() )  dummies += ({ all_inv[i] });
    }
    if( stringp(id) ){
        id = ({ id });
    }
    if(arrayp(id)){
        foreach(object dummy in dummies){
            foreach(string element in id){
                if(answers_to(element,dummy)) return dummy;
            }
        }
    }
    return 0;
}

varargs void AddEnter(string dir, string dest, function pre, function post){
    object ob = GetDummyItem(dir);

    ob->SetEnter(dest, pre, post);
}

string GetEnter(string dir){
    object ob = GetDummyItem(dir);

    if( !ob ){
        return 0;
    }
    else {
        return ob->GetEnter();
    }
}

static mapping GetEnterData(string dir){
    object ob = GetDummyItem(dir);

    if( !ob ){
        return 0;
    }
    else {
        return ob->GetEnterData();
    }
}

varargs string array GetEnters(int i){
    object *obs;
    string *ids;

    obs = ({});
    ids = ({});

    foreach(object item in all_inventory(this_object())){
        if(base_name(item) == LIB_DUMMY){
            obs += ({ item });
        }
    }

    foreach(object ob in obs){
        if( ob->GetEnter() ){
            if(i) ids += ({ ob->GetKeyName() });
            else ids += ob->GetId();
        }
    }
    return ids;
}

mapping GetEnterMap(){
    mixed schlussel;
    mapping EnterMap = ([]);
    object *obs = ({});
    foreach(object item in all_inventory(this_object())){
        if(base_name(item) == LIB_DUMMY){
            obs += ({ item });
        }
    }
    if(!sizeof(obs)) return ([]);
    foreach(object ob in obs){
        if( ob->GetEnter() ){
            schlussel = ob->GetId();
            EnterMap[schlussel] = ob->GetEnter();
        }
    }
    return copy(EnterMap);
}

void RemoveEnter(string dir){
    object ob = GetDummyItem(dir);

    ob->SetEnter(0);
}

void SetEnters(mapping mp){
    foreach(mixed dir, mixed room_or_arr in mp){
        object ob = GetDummyItem(dir);

        if( arrayp(room_or_arr) ){
            ob->SetEnter(room_or_arr...);
        }
        else {
            ob->SetEnter(room_or_arr);
        }
    }
}

string GetEnterMessage(){
    return EnterMessage[0..<2];
}

string SetEnterMessage(string str){
    return (EnterMessage = str + "\n");
}

varargs mapping AddExit(string dir, string dest, function pre, function post){
    if(!stringp(dir)) error("Bad argument 1 to AddExit().\n");
    if(!stringp(dest)) error("Bad argument 2 to AddExit().\n");
    dest = ResolveObjectName(dest);
    Exits[dir] = ([ "room" : dest  ]);
    if( functionp(pre) ) Exits[dir]["pre"] = pre;
    if( functionp(post) ) Exits[dir]["post"] = post;
    return Exits[dir];
}

string GetExit(string str){
    if(!Exits[str]) return 0;
    else return Exits[str]["room"];
}

mapping GetExitData(string str){
    return Exits[str];
}

mapping GetFullExitData(){
    return Exits;
}

mapping GetExitMap(){
    mapping ret = ([]);
    foreach(string key in keys(Exits)){
        ret[key] = Exits[key]["room"];
    }  

    return ret;
}

string array GetExits(){
    return keys(Exits);
}

mapping RemoveExit(string dir){
    if(Exits[dir]) map_delete(Exits, dir);
}

mapping SetExits(mapping mp){
    mixed room_or_arr, dir;

    Exits = ([]);
    foreach(dir, room_or_arr in mp){
        if( arrayp(dir) ){
            string real_dir;

            foreach(real_dir in dir){
                if( arrayp(room_or_arr) ) AddExit(real_dir, room_or_arr...);
                else AddExit(real_dir, room_or_arr);
            }
        }
        else {
            if( stringp(room_or_arr) ) AddExit(dir, room_or_arr);
            else if( arrayp(room_or_arr) ) AddExit(dir, room_or_arr...);
        }
    }
    return Exits;
}

string GetGoMessage(){
    return GoMessage[0..<2];
}

string SetGoMessage(string str){
    return (GoMessage = str + "\n");
}

string GetObviousExits(){
    return Obvious;
}

string SetObviousExits(string str){
    return (Obvious = str);
}

string GetSky(){
    return Sky;
}

string SetSky(string str){
    return (Sky = str);
}

string ResolveObjectName(string file){
    if( file[<2..] == ".c" ) file = file[0..<3];
    return absolute_path(Dir, file);
}
