/** * This handler is for the control of player houses. * @author Pinkfish * @started Fri Jun 23 22:50:02 PDT 2000 */ inherit "/obj/handlers/inherit/nomic_rules"; inherit "/obj/handlers/inherit/case_control"; inherit "/obj/handlers/inherit/citizen_elections"; #define NOMIC_SYSTEM_NO_CLASSES #include <nomic_system.h> #include <player.h> private mapping _motions; private mapping _new_citizens; private int _completion_id; void save_me(); class nomic_motion find_motion(string area, int rule_no, int type); void update_immutables(string area); void send_council_inform(string area, int only_magistrates, string mess); string motion_as_string(int indent, string area, class nomic_motion motion, int brief); void create() { _motions = ([ ]); _new_citizens = ([ ]); citizen_elections::create(); nomic_rules::create(); case_control::create(); // // Check for motion completions at midnight... (well, 2 minutes after). // _completion_id = call_out("check_for_completions", (time() - time() % (24 * 60 * 60) + (24 * 60 * 60) + (2 * 60)) - time()); } /* create() */ /** * This method creates a new area. * @param area the new area */ void create_area(string area) { citizen_elections::create_area(area); nomic_rules::create_area(area); case_control::create_area(area); _motions[area] = ({ }); _new_citizens[area] = ({ }); update_immutables(area); } /* create_area() */ /** * This method updates the immutable laws with the new set of general rules. * @param area the area to update. */ void update_immutables(string area) { string tmp; string type; string file; int int_type; class nomic_rule rule; // // Get rid of the old immutables first. // foreach (rule in query_all_nomic_rules(area)) { if (rule->type == NOMIC_TYPE_IMMUTABLE) { remove_nomic_rule(area, rule->id); } } // // Add in all the default rules. // foreach (file in get_dir(NOMIC_RULE_DEFAULT_DIR)) { if (file_size(NOMIC_RULE_DEFAULT_DIR + "/" + file) > 0) { tmp = read_file(NOMIC_RULE_DEFAULT_DIR + "/" + file); type = explode(tmp, "\n")[0]; tmp = implode(explode(tmp, "\n")[1..], "\n"); switch (type) { case "immutable" : int_type = NOMIC_TYPE_IMMUTABLE; break; case "citizen" : int_type = NOMIC_TYPE_CITIZEN; break; case "general" : int_type = NOMIC_TYPE_GENERAL; break; default : int_type = -1; printf("Ignoring file " + file + "\n"); break; } if (int_type != -1) { add_nomic_rule(area, int_type, area + " administrator", tmp); } } } save_me(); } /* create_area() */ private int query_next_motion_id(string area) { int num; class nomic_motion fluff; num = 1; foreach (fluff in _motions[area]) { if (num <= fluff->identifier) { num = fluff->identifier + 1; } } return num; } /* query_next_motion_id() */ /** * This method puts a new rule up for voting. It is first placed into * the hands of the magistrates, then into the hands of the players. * @param area the area for the rule to be voted on in * @param type the type of the rules * @param text the text associated with the new rule * @return 1 on success, 0 on failure */ int add_create_rule_motion(string area, int type, string text, string creator) { class nomic_motion fluff; if (!is_magistrate_of(area, creator)) { return 0; } fluff = new(class nomic_motion); fluff->motion_type = NOMIC_MOTION_TYPE_RULE_ADD; fluff->rule = new(class nomic_rule); fluff->rule->creator = creator; fluff->rule->text = text; fluff->rule->type = type; fluff->rule->amendments = ({ }); fluff->rule->date_created = time(); fluff->comments = ({ }); fluff->voted = ({ }); fluff->state = NOMIC_STATE_COUNCIL_REVIEW; fluff->date_added = time(); fluff->date_event = time(); fluff->yes_votes = 0; fluff->no_votes = 0; fluff->added_by = creator; fluff->identifier = query_next_motion_id(area); _motions[area] += ({ fluff }); save_me(); send_council_inform(area, 1, creator + " added a new rule motion"); post_magistrate_message(area, "New: Create rule motion", PLAYER_OB->convert_message(motion_as_string(0, area, fluff, 0))); return 1; } /* add_motion_for_voting() */ /** * This method puts up a request to move a rule from one type to another. * @param area the area to do the move in * @param rule_no the rule number to move * @param type the type to move the rule to * @param creator the creator of this request * @return 1 on success, 0 on failure */ int add_move_rule_type_motion(string area, int rule_no, int type, string creator) { class nomic_motion fluff; class nomic_rule bing; bing = query_nomic_rule(area, rule_no); if (!bing) { return 0; } if (bing->type == type) { return 0; } // // First check and see if are alread moveing this rule. // fluff = find_motion(area, rule_no, NOMIC_MOTION_TYPE_RULE_MOVE); if (fluff) { return 0; } fluff = new(class nomic_motion); fluff->motion_type = NOMIC_MOTION_TYPE_RULE_MOVE; fluff->rule = bing; fluff->new_rule_type = type; fluff->comments = ({ }); fluff->voted = ({ }); fluff->state = NOMIC_STATE_COUNCIL_REVIEW; fluff->date_added = time(); fluff->date_event = time(); fluff->yes_votes = 0; fluff->no_votes = 0; fluff->added_by = creator; fluff->identifier = query_next_motion_id(area); _motions[area] += ({ fluff }); save_me(); send_council_inform(area, 1, creator + " added a move rule motion"); post_magistrate_message(area, "New: Move rule motion", PLAYER_OB->convert_message(motion_as_string(0, area, fluff, 0))); return 1; } /* add_move_rule_type() */ /** * This method adds in a request to amend a rule. * @param area the area to put the amendment in * @param rule_no the rule to amend * @param amendment the amendment text * @param creator the person doing the amendment * @return 1 on success, 0 on failure */ int add_amend_rule_motion(string area, int rule_no, string amendment, string creator) { class nomic_motion fluff; class nomic_rule bing; class nomic_amendment amend; bing = query_nomic_rule(area, rule_no); if (!bing) { return 0; } // // First check and see if are already amending this rule. // fluff = find_motion(area, rule_no, NOMIC_MOTION_TYPE_RULE_AMEND); if (fluff) { return 0; } fluff = new(class nomic_motion); fluff->motion_type = NOMIC_MOTION_TYPE_RULE_AMEND; amend = new (class nomic_amendment); amend->amender = creator; amend->text = amendment; amend->date_amended = time(); bing->amendments += ({ amend }); fluff->rule = bing; fluff->comments = ({ }); fluff->voted = ({ }); fluff->state = NOMIC_STATE_COUNCIL_REVIEW; fluff->date_added = time(); fluff->date_event = time(); fluff->yes_votes = 0; fluff->no_votes = 0; fluff->added_by = creator; fluff->identifier = query_next_motion_id(area); _motions[area] += ({ fluff }); save_me(); send_council_inform(area, 1, creator + " added an add rule motion"); post_magistrate_message(area, "New: Ammend rule motion", PLAYER_OB->convert_message(motion_as_string(0, area, fluff, 0))); return 1; } /* add_rule_amendment() */ /** * This method remove a request to amend a rule. * @param area the area to put the amendment in * @param rule_no the rule to amend * @param amendment the amendment text * @param creator the person doing the amendment * @return 1 on success, 0 on failure */ int remove_rule_motion(string area, int rule_no, string creator) { class nomic_motion fluff; class nomic_rule bing; bing = query_nomic_rule(area, rule_no); if (!bing) { return 0; } // // First check and see if are alread removeing this rule. // fluff = find_motion(area, rule_no, NOMIC_MOTION_TYPE_RULE_REMOVE); if (fluff) { return 0; } fluff = new(class nomic_motion); fluff->motion_type = NOMIC_MOTION_TYPE_RULE_REMOVE; fluff->rule = bing; fluff->comments = ({ }); fluff->voted = ({ }); fluff->state = NOMIC_STATE_COUNCIL_REVIEW; fluff->date_added = time(); fluff->date_event = time(); fluff->yes_votes = 0; fluff->no_votes = 0; fluff->added_by = creator; fluff->identifier = query_next_motion_id(area); _motions[area] += ({ fluff }); save_me(); send_council_inform(area, 1, creator + " added a remove rule motion"); post_magistrate_message(area, "New: Remove rule motion", PLAYER_OB->convert_message(motion_as_string(0, area, fluff, 0))); return 1; } /* remove_rule_motion() */ /** * This method returns a nice new rule. * @param area the areat to look in * @param id the id of the new rule to find * @return 1 on success, 0 on failure */ class nomic_motion query_motion(string area, int id) { class nomic_motion fluff; if (!_motions[area]) { return 0; } foreach (fluff in _motions[area]) { if (fluff->identifier == id) { return fluff; } } return 0; } /* query_motion() */ /** * This method returns all the new rules for the specified area. * @param area the area to return the new rules in * @return the array of new rules */ class nomic_motion* query_all_motions(string area) { if (!_motions[area]) { return ({ }); } return copy(_motions[area]); } /* query_all_motions() */ /** * This method finds the specified motion. * @param area the area to look in * @param rule_no the rule number * @param type the type of the motion * @return the motion if successful, 0 if not */ class nomic_motion find_motion(string area, int rule_no, int type) { class nomic_motion motion; foreach (motion in _motions[area]) { if (motion->rule->id == rule_no && motion->motion_type == type) { return motion; } } return 0; } /* find_motion() */ /** * This method adds a comment to the new rule/amendment/whatever. * @param area the area the comment is to go in * @param id the rule update id * @param comment the comment to make * @param commenter the person commenting * @return 1 on success, 0 on failure */ int comment_on_motion(string area, int id, string comment_text, string commenter) { class nomic_comment comment; class nomic_motion bing; bing = query_motion(area, id); if (!bing) { return 0; } if (bing->state != NOMIC_STATE_COUNCIL_REVIEW && bing->state != NOMIC_STATE_COUNCIL_VOTE) { return 0; } comment = new(class nomic_comment); comment->text = comment_text; comment->commenter = commenter; comment->date_commented = time(); bing->comments += ({ comment }); save_me(); return 1; } /* add_motion_update_comment() */ /** * This method adds an amendment to the new rule/amendment/whatever, this * rewrites the text of the ammendment of whatever it is. * @param area the area the amendmend is to go in * @param id the rule update id * @param amendmend the amendmend to make * @param amendmender the person amendmending * @return 1 on success, 0 on failure */ int amend_motion(string area, int id, string amend_text, string amender) { //class nomic_amendment amend; class nomic_motion bing; bing = query_motion(area, id); if (!bing) { return 0; } // This only works in the review phase. if (bing->state != NOMIC_STATE_COUNCIL_REVIEW) { return 0; } /* amend = new(class nomic_amendment); amend->text = amend_text; amend->amender = amender; amend->date_amended = time(); bing->rule->amendments += ({ amend }); */ switch (bing->motion_type) { case NOMIC_MOTION_TYPE_RULE_ADD : bing->rule->text = amend_text; break; case NOMIC_MOTION_TYPE_RULE_REMOVE : case NOMIC_MOTION_TYPE_RULE_MOVE : return 0; case NOMIC_MOTION_TYPE_RULE_AMEND : bing->rule->amendments[<1]->text = amend_text; } save_me(); return 1; } /* add_motion_update_comment() */ /** * This method votes for the specified ammentment/whatever. * @param area the area to vote in * @param id the id to vote for * @param vote_type the type of the vote * @param voter the person is voting * @return 1 on success, 0 on failure */ int vote_for_motion(string area, int id, int vote_type, string voter) { class nomic_motion bing; bing = query_motion(area, id); if (!bing) { return 0; } if (member_array(voter, bing->voted) != -1) { return 0; } if (bing->state != NOMIC_STATE_COUNCIL_VOTE && bing->state != NOMIC_STATE_CITIZEN_VOTE) { return 0; } switch (vote_type) { case NOMIC_VOTE_YES : bing->yes_votes++; break; case NOMIC_VOTE_NO : bing->no_votes++; break; case NOMIC_VOTE_ABSTAIN : bing->abstain_votes++; break; default : return 0; } bing->voted += ({ voter }); save_me(); return 1; } /* vote_for_motion() */ /** * This method checks to see if the person has voted for the specified * new rule/amendment/whatever * @param area the area to check * @param id the id to check * @param voter the person to check for voting * @return 1 on success, 0 on failure */ int has_voted_for_motion(string area, int id, string voter) { class nomic_motion bing; bing = query_motion(area, id); if (!bing) { return 0; } return member_array(voter, bing->voted) != -1; } /* has_voted_for_motion() */ /** * This method returns the current set of votes for the specied new rule. * @param area the area to lookup the votes in * @param id the idea to check * @return ({ yes, no, abstain }) */ int* query_votes_for_motion(string area, int id) { class nomic_motion bing; bing = query_motion(area, id); if (!bing) { return 0; } return ({ bing->yes_votes, bing->no_votes, bing->abstain_votes }); } /* query_voted_for_motions() */ private void remove_motion(string area, class nomic_motion motion) { int i; for (i = 0; i < sizeof(_motions[area]); i++) { if (_motions[area][i] == motion) { _motions[area] = _motions[area][0..i-1] + _motions[area][i+1..]; } } } /* remove_motion() */ /** * This method sends informs to all the specified council of some sort of * nifty council event. * @param area the area the council is for * @param only_magistrates only send it to the magistrates? * @param mess the message to send */ void send_council_inform(string area, int only_magistrates, string mess) { object *obs; if (only_magistrates) { obs = filter(users(), (: is_magistrate_of($2, $1->query_name()) :), area); } else { obs = filter(users(), (: is_citizen_of($2, $1->query_name()) :), area); } call_other(obs, "event_inform", this_object(), mess, "council"); } /* send_council_inform() */ /** * This method figures out the compleion time for the specified motion. * @param area the area to check * @param the motion in the area * @return the completion time */ int query_completion_time(string area, class nomic_motion motion) { return (motion->date_event - motion->date_event % (24 * 60 * 60) + 7 * (24 * 60 * 60)); } /* query_completion_time() */ /** * This method completes the vote on the specified new rule. * @param area the area to complete the rule in * @param id the id to complete * @return 1 on success, 0 on failure */ int complete_motion(string area, int id) { class nomic_motion bing; class nomic_motion motion; int passed; int new_num; string mess; int only_magistrates; string voting_result; bing = query_motion(area, id); if (!bing) { return 0; } passed = bing->yes_votes > bing->no_votes; voting_result = "Voting Results:\n" " Yes: " + bing->yes_votes + "\n" " No: " + bing->no_votes + "\n" "Abstain: " + bing->abstain_votes + "\n"; mess = "Motion added by " + bing->added_by + " to "; switch (bing->motion_type) { case NOMIC_MOTION_TYPE_RULE_ADD : mess += "add a new rule"; break; case NOMIC_MOTION_TYPE_RULE_REMOVE : mess += "remove rule " + bing->rule->id; break; case NOMIC_MOTION_TYPE_RULE_MOVE : mess += "move rule " + bing->rule->id; break; case NOMIC_MOTION_TYPE_RULE_AMEND : mess += "amend rule " + bing->rule->id; break; } if (passed || bing->state == NOMIC_STATE_COUNCIL_REVIEW) { switch (bing->state) { case NOMIC_STATE_COUNCIL_REVIEW : bing->yes_votes = 0; bing->no_votes = 0; bing->abstain_votes = 0; bing->voted = ({ }); bing->state = NOMIC_STATE_COUNCIL_VOTE; mess += " is open for magistrates to vote"; only_magistrates = 1; post_magistrate_message(area, "Motion now in voting phase", PLAYER_OB->convert_message(motion_as_string(0, area, bing, 0))); break; case NOMIC_STATE_COUNCIL_VOTE : // // Pass this and set all the defaults back up. // bing->yes_votes = 0; bing->no_votes = 0; bing->abstain_votes = 0; bing->voted = ({ }); bing->state = NOMIC_STATE_CITIZEN_VOTE; bing->comments = ({ }); mess += " is open for citizens to vote"; post_magistrate_message(area, "Law passed onto citizens", PLAYER_OB->convert_message(rule_as_string(0, bing->rule)) + "\n\n" + voting_result); post_citizen_message(area, "New motion for voting on", PLAYER_OB->convert_message(motion_as_string(0, area, bing, 0))); break; case NOMIC_STATE_CITIZEN_VOTE : switch (bing->motion_type) { case NOMIC_MOTION_TYPE_RULE_ADD : add_nomic_rule_class(area, bing->rule, bing->motion_type); break; case NOMIC_MOTION_TYPE_RULE_AMEND : change_nomic_rule(area, bing->rule); break; case NOMIC_MOTION_TYPE_RULE_MOVE : new_num = move_nomic_rule(area, bing->rule, bing->new_rule_type); foreach (motion in _motions[area]) { if (motion->rule->id == bing->rule->id) { motion->rule->id = new_num; } } break; case NOMIC_MOTION_TYPE_RULE_REMOVE : remove_nomic_rule(area, bing->rule->id); break; } remove_motion(area, bing); mess += " is now law"; post_citizen_message(area, "New Law!", PLAYER_OB->convert_message(rule_as_string(0, bing->rule))); post_magistrate_message(area, "New Law!", PLAYER_OB->convert_message(rule_as_string(0, bing->rule)) + "\n\n" + voting_result); break; } bing->date_event = time(); } else { if (bing->state == NOMIC_STATE_COUNCIL_VOTE) { mess += " not passed by the magistrates"; only_magistrates = 1; post_magistrate_message(area, "Law not passed onto citizens", rule_as_string(0, bing->rule) + "\n\n" + voting_result); } else { mess += " not ratified by the citizens"; only_magistrates = 0; post_citizen_message(area, "Law not passed", rule_as_string(0, bing->rule)); post_magistrate_message(area, "Law not passed", rule_as_string(0, bing->rule) + "\n\n" + voting_result); } remove_motion(area, bing); } save_me(); send_council_inform(area, only_magistrates, mess); return 1; } /* complete_motion() */ /** * This method figures out all the current things and when they completed. */ void check_for_completions() { string area; class nomic_motion* bits; class nomic_motion motion; foreach (area, bits in _motions) { foreach (motion in bits) { if (query_completion_time(area, motion) < time()) { complete_motion(area, motion->identifier); } } } remove_call_out(_completion_id); _completion_id = call_out("check_for_completions", (time() - time() % (24 * 60 * 60) + (24 * 60 * 60)) - time()); // // Check the cases to see if they want to mess around too. // update_recently_closed_cases(); } /* check_for_completions() */ /** * This method returns the new motion as a string. * @param indent the indentation to putin front of all the lines * @param area the area the motion is defined in * @param motion the new motion to turn into a styring * @param brief disable comments * @return the string version of the motion */ string motion_as_string(int indent, string area, class nomic_motion motion, int brief) { string ret; class nomic_comment comment; ret = ""; switch (motion->motion_type) { case NOMIC_MOTION_TYPE_RULE_ADD : ret += "$I$" + (indent + 5) + "=" + sprintf("%*s", indent, "") + motion->identifier + ") Add new rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "\n"; break; case NOMIC_MOTION_TYPE_RULE_REMOVE : ret += "$I$" + (indent + 5) + "=" + sprintf("%*s", indent, "") + motion->identifier + ") Remove rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "\n"; break; case NOMIC_MOTION_TYPE_RULE_MOVE : ret += "$I$" + (indent + 5) + "=" + sprintf("%*s", indent, "") + motion->identifier + ") Transmogrify rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "\n"; break; case NOMIC_MOTION_TYPE_RULE_AMEND : ret += "$I$" + (indent + 5) + "=" + sprintf("%*s", indent, "") + motion->identifier + ") Amend rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "\n"; break; } ret += rule_as_string(5 + indent, motion->rule); if (sizeof(motion->comments)) { if (!brief) { ret += "$I$" + (indent + 5) + "= Comments:\n"; foreach (comment in motion->comments) { ret += "$I$" + (indent + 11) + "=" + sprintf("%*s", indent + 3, "") + capitalize(comment->commenter) + " (" + ctime(comment->date_commented) + "):\n" + comment->text + "\n"; } } else { ret += "$I$0=" + sprintf("%*s", indent, "") + " " + sizeof(motion->comments) + " Comments.\n"; } } else { ret += "$I$0=" + sprintf("%*s", indent, "") + " No Comments.\n"; } switch (motion->state) { case NOMIC_STATE_COUNCIL_REVIEW : ret += "$I$0=" + sprintf("%*s", indent, "") + "The item is currently being reviewed by the magistrates.\n"; break; case NOMIC_STATE_COUNCIL_VOTE : ret += "$I$0=" + sprintf("%*s", indent, "") + "The item is currently being voted on by the magistrates.\n"; break; case NOMIC_STATE_CITIZEN_VOTE : ret += "$I$0=" + sprintf("%*s", indent, "") + "The item is currently being ratified by the citizens.\n"; break; } // // The next stage! // ret += "$I$0=" + sprintf("%*s", indent, "") + "The item will go onto the next stage at " + ctime(query_completion_time(area, motion)) + ".\n"; return ret; } /* motion_as_string() */ /** * This method turns a rule into html. * @param rule the rule to turn into html * @return the rule as a string */ string rule_as_html(class nomic_rule rule) { string ret; class nomic_amendment amend; ret = "<i><b>" + (rule->id?rule->id+"":"(" + NOMIC_HANDLER->query_type_name(rule->type) + ")") + "</b> by " + capitalize(rule->creator) + " (" + ctime(rule->date_created) + "):</i>\n" "<p style=\"margin-left: 30pt;margin-top: 5pt\">\n" + replace_string(rule->text, "\n", "<br>") + "<br>\n"; foreach (amend in rule->amendments) { ret += "<i><b>Ammendment</b> by " + capitalize(amend->amender) + " (" + ctime(amend->date_amended) + "):</i>\n" "<p style=\"margin-left: 60pt;margin-top: 5pt\">\n" + replace_string(amend->text, "\n", "<br>") + "\n</p>\n"; } ret += "</p>\n"; return ret; } /* rule_as_html() */ /** * This method returns the new motion as a string. * @param area the area the motion is defined in * @param motion the new motion to turn into a styring * @return the string version of the motion */ string motion_as_html(string area, class nomic_motion motion) { string ret; class nomic_comment comment; ret = ""; switch (motion->motion_type) { case NOMIC_MOTION_TYPE_RULE_ADD : ret += "<b>" + motion->identifier + ") Add new rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "</b>\n"; break; case NOMIC_MOTION_TYPE_RULE_REMOVE : ret += "<b>" + motion->identifier + ") Remove rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "</b>\n"; break; case NOMIC_MOTION_TYPE_RULE_MOVE : ret += "<b>" + motion->identifier + ") Transmogrify rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "</b>\n"; break; case NOMIC_MOTION_TYPE_RULE_AMEND : ret += "<b>" + motion->identifier + ") Amend rule; added by " + capitalize(motion->added_by) + " at " + ctime(motion->date_added) + "</b>\n"; break; } ret += "<ul>" + rule_as_html(motion->rule) + "</ul>"; if (sizeof(motion->comments)) { ret += "<b>Comments:</b><br>\n"; foreach (comment in motion->comments) { ret += ctime(comment->date_commented) + "):<br>\n" + comment->text + "<p>\n"; } } switch (motion->state) { case NOMIC_STATE_COUNCIL_REVIEW : ret += "The item is currently being reviewed by the magistrates.\n"; break; case NOMIC_STATE_COUNCIL_VOTE : ret += "The item is currently being voted on by the magistrates.\n"; break; case NOMIC_STATE_CITIZEN_VOTE : ret += "The item is currently being ratified by the citizens.\n"; break; } // // The next stage! // ret += "<br>The item will go onto the next stage at " + ctime(query_completion_time(area, motion)) + ".\n"; return ret; } /* motion_as_string() */