Allow creation of new corps.
This commit is contained in:
parent
158b590c35
commit
929a64f93e
@ -18,6 +18,7 @@ use crate::models::{
|
|||||||
},
|
},
|
||||||
task::{Task, TaskParse},
|
task::{Task, TaskParse},
|
||||||
consent::{Consent, ConsentType},
|
consent::{Consent, ConsentType},
|
||||||
|
corp::{Corp, CorpId, CorpMembership},
|
||||||
};
|
};
|
||||||
use tokio_postgres::types::ToSql;
|
use tokio_postgres::types::ToSql;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
@ -788,6 +789,54 @@ impl DBTrans {
|
|||||||
&serde_json::to_value(details)?]).await?;
|
&serde_json::to_value(details)?]).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_corp_by_name(&self, name: &str) -> DResult<Option<(CorpId, Corp)>> {
|
||||||
|
Ok(match self.pg_trans()?
|
||||||
|
.query_opt("SELECT corp_id, details FROM corps WHERE LOWER(details->>'name') = $1",
|
||||||
|
&[&name.to_lowercase()])
|
||||||
|
.await? {
|
||||||
|
None => None,
|
||||||
|
Some(row) =>
|
||||||
|
Some(
|
||||||
|
(CorpId(row.get("corp_id")), serde_json::from_value(row.get("details"))?)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_corp(&self, details: &Corp) -> DResult<CorpId> {
|
||||||
|
let id = self.pg_trans()?
|
||||||
|
.query_one("INSERT INTO corps (details) VALUES ($1) RETURNING corp_id", &[&serde_json::to_value(details)?]).await?
|
||||||
|
.get("corp_id");
|
||||||
|
Ok(CorpId(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn upsert_corp_membership(&self, corp: &CorpId, username: &str, details: &CorpMembership) -> DResult<()> {
|
||||||
|
self.pg_trans()?
|
||||||
|
.execute("INSERT INTO corp_membership (corp_id, member_username, details) \
|
||||||
|
VALUES ($1, $2, $3) \
|
||||||
|
ON CONFLICT (corp_id, member_username) DO UPDATE SET \
|
||||||
|
details = excluded.details",
|
||||||
|
&[&corp.0, &username.to_lowercase(),
|
||||||
|
&serde_json::to_value(details)?]
|
||||||
|
).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_corp_memberships_for_user(&self, username: &str) -> DResult<Vec<(CorpId, String, CorpMembership)>> {
|
||||||
|
Ok(self.pg_trans()?
|
||||||
|
.query("SELECT m.corp_id, c.details->>'name', m.details FROM corp_membership m \
|
||||||
|
JOIN corps c ON c.corp_id = m.corp_id WHERE m.member_username = $1 \
|
||||||
|
AND m.details->>'joined_at' IS NOT NULL", &[&username.to_lowercase()])
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|row|
|
||||||
|
match serde_json::from_value(row.get(2)) {
|
||||||
|
Err(_) => None,
|
||||||
|
Ok(j) =>
|
||||||
|
Some((CorpId(row.get(0)), row.get(1), j))
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn commit(mut self: Self) -> DResult<()> {
|
pub async fn commit(mut self: Self) -> DResult<()> {
|
||||||
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
|
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
|
||||||
|
@ -30,14 +30,16 @@ pub mod movement;
|
|||||||
mod page;
|
mod page;
|
||||||
pub mod parsing;
|
pub mod parsing;
|
||||||
mod quit;
|
mod quit;
|
||||||
mod register;
|
pub mod register;
|
||||||
pub mod say;
|
pub mod say;
|
||||||
mod score;
|
mod score;
|
||||||
|
mod sign;
|
||||||
mod status;
|
mod status;
|
||||||
pub mod use_cmd;
|
pub mod use_cmd;
|
||||||
mod whisper;
|
mod whisper;
|
||||||
mod who;
|
mod who;
|
||||||
pub mod wield;
|
pub mod wield;
|
||||||
|
mod write;
|
||||||
|
|
||||||
pub struct VerbContext<'l> {
|
pub struct VerbContext<'l> {
|
||||||
pub session: &'l ListenerSession,
|
pub session: &'l ListenerSession,
|
||||||
@ -145,6 +147,8 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
|||||||
"sc" => score::VERB,
|
"sc" => score::VERB,
|
||||||
"score" => score::VERB,
|
"score" => score::VERB,
|
||||||
|
|
||||||
|
"sign" => sign::VERB,
|
||||||
|
|
||||||
"st" => status::VERB,
|
"st" => status::VERB,
|
||||||
"stat" => status::VERB,
|
"stat" => status::VERB,
|
||||||
"status" => status::VERB,
|
"status" => status::VERB,
|
||||||
@ -157,6 +161,7 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
|||||||
|
|
||||||
"wield" => wield::VERB,
|
"wield" => wield::VERB,
|
||||||
"who" => who::VERB,
|
"who" => who::VERB,
|
||||||
|
"write" => write::VERB,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> {
|
fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> {
|
||||||
|
@ -60,6 +60,9 @@ impl UserVerb for Verb {
|
|||||||
if ctx.trans.find_by_username(username).await?.is_some() {
|
if ctx.trans.find_by_username(username).await?.is_some() {
|
||||||
user_error("Username already exists".to_owned())?;
|
user_error("Username already exists".to_owned())?;
|
||||||
}
|
}
|
||||||
|
if ctx.trans.find_corp_by_name(username).await?.is_some() {
|
||||||
|
user_error("Username clashes with existing corp name".to_owned())?;
|
||||||
|
}
|
||||||
if password.len() < 6 {
|
if password.len() < 6 {
|
||||||
user_error("Password must be 6 characters long or longer".to_owned())?;
|
user_error("Password must be 6 characters long or longer".to_owned())?;
|
||||||
} else if !validator::validate_email(email) {
|
} else if !validator::validate_email(email) {
|
||||||
|
37
blastmud_game/src/message_handler/user_commands/sign.rs
Normal file
37
blastmud_game/src/message_handler/user_commands/sign.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use super::{VerbContext, UserVerb, UserVerbRef, UResult, user_error, get_player_item_or_fail, search_item_for_user};
|
||||||
|
use crate::{
|
||||||
|
db::ItemSearchParams,
|
||||||
|
static_content::possession_type::possession_data,
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
pub struct Verb;
|
||||||
|
#[async_trait]
|
||||||
|
impl UserVerb for Verb {
|
||||||
|
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> {
|
||||||
|
let item_name = remaining.trim();
|
||||||
|
if item_name == "" {
|
||||||
|
user_error("Sign what? Try: sign something".to_owned())?;
|
||||||
|
}
|
||||||
|
let player_item = get_player_item_or_fail(ctx).await?;
|
||||||
|
let item = search_item_for_user(ctx, &ItemSearchParams {
|
||||||
|
include_contents: true,
|
||||||
|
..ItemSearchParams::base(&player_item, item_name)
|
||||||
|
}).await?;
|
||||||
|
if item.item_type != "possession" {
|
||||||
|
user_error("You can't sign that!".to_owned())?;
|
||||||
|
}
|
||||||
|
let handler = match item.possession_type.as_ref()
|
||||||
|
.and_then(|pt| possession_data().get(pt))
|
||||||
|
.and_then(|pd| pd.sign_handler) {
|
||||||
|
None => user_error("You can't sign that!".to_owned())?,
|
||||||
|
Some(h) => h
|
||||||
|
};
|
||||||
|
|
||||||
|
handler.cmd(ctx, &player_item, &item).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static VERB_INT: Verb = Verb;
|
||||||
|
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
37
blastmud_game/src/message_handler/user_commands/write.rs
Normal file
37
blastmud_game/src/message_handler/user_commands/write.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use super::{VerbContext, UserVerb, UserVerbRef, UResult, user_error, get_player_item_or_fail, search_item_for_user};
|
||||||
|
use crate::{
|
||||||
|
db::ItemSearchParams,
|
||||||
|
static_content::possession_type::possession_data,
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
pub struct Verb;
|
||||||
|
#[async_trait]
|
||||||
|
impl UserVerb for Verb {
|
||||||
|
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, remaining: &str) -> UResult<()> {
|
||||||
|
let (write_what_raw, on_what_raw) = match remaining.rsplit_once(" on ") {
|
||||||
|
None => user_error("Write on what? Try write something on something".to_owned())?,
|
||||||
|
Some(v) => v
|
||||||
|
};
|
||||||
|
let player_item = get_player_item_or_fail(ctx).await?;
|
||||||
|
let item = search_item_for_user(ctx, &ItemSearchParams {
|
||||||
|
include_contents: true,
|
||||||
|
..ItemSearchParams::base(&player_item, on_what_raw.trim())
|
||||||
|
}).await?;
|
||||||
|
if item.item_type != "possession" {
|
||||||
|
user_error("You can't write on that!".to_owned())?;
|
||||||
|
}
|
||||||
|
let handler = match item.possession_type.as_ref()
|
||||||
|
.and_then(|pt| possession_data().get(pt))
|
||||||
|
.and_then(|pd| pd.write_handler) {
|
||||||
|
None => user_error("You can't write on that!".to_owned())?,
|
||||||
|
Some(h) => h
|
||||||
|
};
|
||||||
|
|
||||||
|
handler.write_cmd(ctx, &player_item, &item, write_what_raw.trim()).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static VERB_INT: Verb = Verb;
|
||||||
|
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
@ -3,3 +3,4 @@ pub mod user;
|
|||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod task;
|
pub mod task;
|
||||||
pub mod consent;
|
pub mod consent;
|
||||||
|
pub mod corp;
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub enum CorpPermission {
|
||||||
|
Holder, // Implies all permissions.
|
||||||
|
Hire,
|
||||||
|
Fire,
|
||||||
|
ChangeJobTitle,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CorpId(pub i64);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Corp {
|
pub struct Corp {
|
||||||
@ -7,4 +18,36 @@ pub struct Corp {
|
|||||||
// allow_combat off. This will allow duly authorised corp members to
|
// allow_combat off. This will allow duly authorised corp members to
|
||||||
// consent to combat with other corps, and have it apply to members.
|
// consent to combat with other corps, and have it apply to members.
|
||||||
pub allow_combat_required: bool,
|
pub allow_combat_required: bool,
|
||||||
|
pub member_permissions: Vec<CorpPermission>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Corp {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: "Unset".to_owned(),
|
||||||
|
allow_combat_required: false,
|
||||||
|
member_permissions: vec!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct CorpMembership {
|
||||||
|
pub invited_at: Option<DateTime<Utc>>,
|
||||||
|
pub joined_at: Option<DateTime<Utc>>,
|
||||||
|
pub permissions: Vec<CorpPermission>,
|
||||||
|
pub allow_combat: bool,
|
||||||
|
pub job_title: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CorpMembership {
|
||||||
|
fn default() -> CorpMembership {
|
||||||
|
CorpMembership {
|
||||||
|
invited_at: None,
|
||||||
|
joined_at: None,
|
||||||
|
permissions: vec!(),
|
||||||
|
allow_combat: false,
|
||||||
|
job_title: "Employee".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,13 @@ use serde::{Serialize, Deserialize};
|
|||||||
use crate::{
|
use crate::{
|
||||||
models::item::{SkillType, Item, Pronouns},
|
models::item::{SkillType, Item, Pronouns},
|
||||||
models::consent::ConsentType,
|
models::consent::ConsentType,
|
||||||
|
message_handler::user_commands::{UResult, VerbContext},
|
||||||
};
|
};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use super::species::BodyPart;
|
use super::species::BodyPart;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
mod fangs;
|
mod fangs;
|
||||||
mod antenna_whip;
|
mod antenna_whip;
|
||||||
@ -80,7 +82,7 @@ pub enum UseEffect {
|
|||||||
BroadcastMessage { messagef: Box<dyn Fn(&Item, &Item, &Item) -> (String, String) + Sync + Send>},
|
BroadcastMessage { messagef: Box<dyn Fn(&Item, &Item, &Item) -> (String, String) + Sync + Send>},
|
||||||
// skill_multiplier is always positive - sign flipped for crit fails.
|
// skill_multiplier is always positive - sign flipped for crit fails.
|
||||||
ChangeTargetHealth { delay_secs: u64, base_effect: i64, skill_multiplier: f64,
|
ChangeTargetHealth { delay_secs: u64, base_effect: i64, skill_multiplier: f64,
|
||||||
max_effect: i64, message: Box<dyn Fn(&Item) -> (String, String) + Sync + Send> }
|
max_effect: i64, message: Box<dyn Fn(&Item) -> (String, String) + Sync + Send> },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UseData {
|
pub struct UseData {
|
||||||
@ -109,6 +111,16 @@ impl Default for UseData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait WriteHandler {
|
||||||
|
async fn write_cmd(&self, ctx: &mut VerbContext, player: &Item, on_what: &Item, write_what: &str) -> UResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait ArglessHandler {
|
||||||
|
async fn cmd(&self, ctx: &mut VerbContext, player: &Item, what: &Item) -> UResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PossessionData {
|
pub struct PossessionData {
|
||||||
pub weapon_data: Option<WeaponData>,
|
pub weapon_data: Option<WeaponData>,
|
||||||
pub display: &'static str,
|
pub display: &'static str,
|
||||||
@ -121,6 +133,8 @@ pub struct PossessionData {
|
|||||||
pub use_data: Option<UseData>,
|
pub use_data: Option<UseData>,
|
||||||
pub becomes_on_spent: Option<PossessionType>,
|
pub becomes_on_spent: Option<PossessionType>,
|
||||||
pub weight: u64,
|
pub weight: u64,
|
||||||
|
pub write_handler: Option<&'static (dyn WriteHandler + Sync + Send)>,
|
||||||
|
pub sign_handler: Option<&'static (dyn ArglessHandler + Sync + Send)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PossessionData {
|
impl Default for PossessionData {
|
||||||
@ -137,6 +151,8 @@ impl Default for PossessionData {
|
|||||||
charge_data: None,
|
charge_data: None,
|
||||||
becomes_on_spent: None,
|
becomes_on_spent: None,
|
||||||
use_data: None,
|
use_data: None,
|
||||||
|
write_handler: None,
|
||||||
|
sign_handler: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,127 @@
|
|||||||
use super::{PossessionData, UseData, UseEffect};
|
use super::{PossessionData, WriteHandler, ArglessHandler};
|
||||||
use crate::models::item::{SkillType, ItemSpecialData};
|
use crate::{
|
||||||
|
models::{
|
||||||
|
item::{Item, ItemSpecialData},
|
||||||
|
corp::{Corp, CorpMembership, CorpPermission},
|
||||||
|
},
|
||||||
|
message_handler::user_commands::{
|
||||||
|
register::is_invalid_username,
|
||||||
|
parsing::parse_username,
|
||||||
|
user_error,
|
||||||
|
UResult,
|
||||||
|
VerbContext,
|
||||||
|
},
|
||||||
|
services::broadcast_to_room,
|
||||||
|
};
|
||||||
use ansi::ansi;
|
use ansi::ansi;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
use super::PossessionType::*;
|
use super::PossessionType::*;
|
||||||
|
|
||||||
|
pub struct CorpLicenceHandler {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WriteHandler for CorpLicenceHandler {
|
||||||
|
async fn write_cmd(&self, ctx: &mut VerbContext, _player: &Item, on_what: &Item, write_what: &str) -> UResult<()> {
|
||||||
|
let name = match parse_username(write_what) {
|
||||||
|
Err(e) => user_error("Invalid corp name: ".to_owned() + e)?,
|
||||||
|
Ok((_, rest)) if rest != "" =>
|
||||||
|
user_error("No spaces allowed in corp names!".to_owned())?,
|
||||||
|
Ok((name, _)) => name
|
||||||
|
};
|
||||||
|
if is_invalid_username(name) {
|
||||||
|
user_error("Sorry, that corp name isn't allowed. Try another".to_owned())?;
|
||||||
|
}
|
||||||
|
if ctx.trans.find_by_username(name).await?.is_some() {
|
||||||
|
user_error("Corp name clashes with existing user name".to_owned())?;
|
||||||
|
}
|
||||||
|
if ctx.trans.find_corp_by_name(&name).await?.is_some() {
|
||||||
|
user_error("Corp name already taken!".to_owned())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut item_clone = on_what.clone();
|
||||||
|
item_clone.special_data = Some(ItemSpecialData::ItemWriting {
|
||||||
|
text: name.to_owned()
|
||||||
|
});
|
||||||
|
ctx.trans.save_item_model(&item_clone).await?;
|
||||||
|
ctx.trans.queue_for_session(ctx.session, Some(&format!(ansi!(
|
||||||
|
"The pencil makes a scratching sound as you mark the paper with the attached \
|
||||||
|
pencil and write \"{}\" on it. [Hint: Try the <bold>use<reset> command to submit \
|
||||||
|
your signed paperwork and register the corporation, or <bold>write<reset> again \
|
||||||
|
to erase and change the name].\n"), name))).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ArglessHandler for CorpLicenceHandler {
|
||||||
|
async fn cmd(&self, ctx: &mut VerbContext, player: &Item, what: &Item) -> UResult<()> {
|
||||||
|
let name = match what.special_data.as_ref() {
|
||||||
|
Some(ItemSpecialData::ItemWriting { text }) => text,
|
||||||
|
_ => user_error("You have to write your corp's name on it first!".to_owned())?
|
||||||
|
};
|
||||||
|
if ctx.trans.find_by_username(&name).await?.is_some() {
|
||||||
|
user_error("Corp name clashes with existing user name".to_owned())?;
|
||||||
|
}
|
||||||
|
if ctx.trans.find_corp_by_name(&name).await?.is_some() {
|
||||||
|
user_error("Corp name already taken!".to_owned())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.trans.get_corp_memberships_for_user(&player.item_code).await?.len() >= 5 {
|
||||||
|
user_error("You can't be in more than 5 corps".to_owned())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcast_to_room(ctx.trans, &player.location, None,
|
||||||
|
&format!(
|
||||||
|
"{} signs a contract establishing {} as a corp\n",
|
||||||
|
&player.display_for_sentence(true, 1, true),
|
||||||
|
name
|
||||||
|
),
|
||||||
|
Some(
|
||||||
|
&format!("{} signs a contract establishing {} as a corp\n",
|
||||||
|
&player.display_for_sentence(false, 1, true),
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)).await?;
|
||||||
|
let corp_id = ctx.trans.create_corp(&Corp {
|
||||||
|
name: name.to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
}).await?;
|
||||||
|
ctx.trans.upsert_corp_membership(
|
||||||
|
&corp_id, &player.item_code,
|
||||||
|
&CorpMembership {
|
||||||
|
joined_at: Some(Utc::now()),
|
||||||
|
permissions: vec!(CorpPermission::Holder),
|
||||||
|
allow_combat: true,
|
||||||
|
job_title: "Founder".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
}).await?;
|
||||||
|
|
||||||
|
let mut what_mut = what.clone();
|
||||||
|
what_mut.possession_type = Some(CertificateOfIncorporation);
|
||||||
|
let cp_data = cert_data();
|
||||||
|
what_mut.display = cp_data.display.to_owned();
|
||||||
|
what_mut.details = Some(cp_data.details.to_owned());
|
||||||
|
ctx.trans.save_item_model(&what_mut).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CORP_LICENCE_HANDLER: CorpLicenceHandler = CorpLicenceHandler {};
|
||||||
|
|
||||||
pub fn data() -> PossessionData {
|
pub fn data() -> PossessionData {
|
||||||
PossessionData {
|
PossessionData {
|
||||||
display: "new corp licence",
|
display: "new corp licence",
|
||||||
details: ansi!("A blank form that you can <bold>use<reset> to establish a new corp. It rests on a clipboard with a pencil attached by a chain. There is a space to <bold>write<reset> on it [try <bold>write Blah on licence<reset> followed by <bold>use licence<reset> to create a corp named Blah]"),
|
details: ansi!("A blank form that you can <bold>use<reset> to establish a new corp. It rests on a clipboard with a pencil attached by a chain. There is a space to <bold>write<reset> on it [try <bold>write Blah on licence<reset> followed by <bold>sign licence<reset> to create a corp named Blah]"),
|
||||||
aliases: vec!("form", "license", "licence", "new"),
|
aliases: vec!("form", "license", "licence", "new"),
|
||||||
use_data: Some(UseData {
|
|
||||||
uses_skill: SkillType::Persuade,
|
|
||||||
diff_level: 4.0,
|
|
||||||
crit_fail_effects: vec!(),
|
|
||||||
fail_effects: vec!(),
|
|
||||||
success_effects: vec!(
|
|
||||||
UseEffect::BroadcastMessage {
|
|
||||||
messagef: Box::new(|player, _item, _target| (
|
|
||||||
format!(
|
|
||||||
"{} signs a contract establishing Blah as a corp\n",
|
|
||||||
&player.display_for_sentence(true, 1, true),
|
|
||||||
),
|
|
||||||
format!("{} signs a contract establishing Blah as a corp\n",
|
|
||||||
&player.display_for_sentence(false, 1, true),
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
),
|
|
||||||
errorf: Box::new(
|
|
||||||
|item, _target|
|
|
||||||
match item.special_data {
|
|
||||||
Some(ItemSpecialData::ItemWriting { .. }) => None,
|
|
||||||
_ => Some("You have to your corp's name on it first!".to_owned())
|
|
||||||
}),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
weight: 10,
|
weight: 10,
|
||||||
becomes_on_spent: Some(CertificateOfIncorporation),
|
becomes_on_spent: Some(CertificateOfIncorporation),
|
||||||
|
write_handler: Some(&CORP_LICENCE_HANDLER),
|
||||||
|
sign_handler: Some(&CORP_LICENCE_HANDLER),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,13 +58,14 @@ CREATE TABLE corps (
|
|||||||
corp_id BIGSERIAL NOT NULL PRIMARY KEY,
|
corp_id BIGSERIAL NOT NULL PRIMARY KEY,
|
||||||
details JSONB NOT NULL
|
details JSONB NOT NULL
|
||||||
);
|
);
|
||||||
CREATE INDEX corp_by_name ON corps((details->>'name'));
|
CREATE INDEX corp_by_name ON corps((LOWER(details->>'name')));
|
||||||
CREATE TABLE corp_membership (
|
CREATE TABLE corp_membership (
|
||||||
corp_id BIGSERIAL NOT NULL REFERENCES corps(corp_id),
|
corp_id BIGSERIAL NOT NULL REFERENCES corps(corp_id),
|
||||||
member_username TEXT NOT NULL REFERENCES users(username),
|
member_username TEXT NOT NULL REFERENCES users(username),
|
||||||
details JSONB NOT NULL,
|
details JSONB NOT NULL,
|
||||||
PRIMARY KEY (corp_id, member_username)
|
PRIMARY KEY (corp_id, member_username)
|
||||||
);
|
);
|
||||||
|
CREATE INDEX corp_membership_by_username ON corp_membership(member_username);
|
||||||
|
|
||||||
CREATE TABLE user_consent (
|
CREATE TABLE user_consent (
|
||||||
consenting_user TEXT NOT NULL REFERENCES users(username),
|
consenting_user TEXT NOT NULL REFERENCES users(username),
|
||||||
|
Loading…
Reference in New Issue
Block a user