rpg_stat/
npc.rs

1/*
2# NPC - Non-Player Characters
3
4These are what make the grind worth it sometimes.  When the interaction is quirky and humorous, as well as decently random (see Class based below) it makes the environment more fun.
5
6Fortunately we live in a world where there are tons of quotes in the public domain (like the entire KJV Bible or Shakespear as examples of massive English works with numerous well understood quotes)
7
8We also provide `AI` Logic as well.
9*/
10use std::fmt;
11use std::fmt::Debug;
12extern crate num;
13use serde::{Deserialize, Serialize};
14
15#[cfg(feature = "fltkform")]
16use fltk::{prelude::*, *};
17#[cfg(feature = "fltkform")]
18use fltk_form_derive::*;
19#[cfg(feature = "fltkform")]
20use fltk_form::FltkForm;
21
22// our module
23use crate::random::Random;
24
25/*
26# State
27The state the individual is in.
28There are two states `Broken` and `Ordered`  These are assesments of the individual's alignment and choices.  If their choices match the Alignment, the State becomes `Ordered` when out of sync it becomes `Broken`
29*/
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
31#[cfg_attr(feature = "fltkform", derive(FltkForm))]
32pub enum State {
33    /// Alignment does not match actions
34    Broken,
35    /// Actions match Alignment
36    Ordered,
37}
38impl fmt::Display for State {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        let v:String;
41        match *self {
42            State::Broken => v = String::from("Broken"),
43            State::Ordered => v = String::from("Ordered"),
44
45        }
46        write!(f, "{}", v.as_str())
47    }
48}
49impl Default for State {
50    fn default() -> Self {
51        Self::Ordered
52    }
53}
54
55/*
56
57*/
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
59#[cfg_attr(feature = "fltkform", derive(FltkForm))]
60pub enum Purpose {
61    Random,
62    Story,
63    Task,
64}
65impl fmt::Display for Purpose {
66    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67        let v:String;
68        match *self {
69            Purpose::Random => v = String::from("Random"),
70            Purpose::Story => v = String::from("Story"),
71            Purpose::Task => v = String::from("Task"),
72
73        }
74        write!(f, "{}", v.as_str())
75    }
76}
77impl Default for Purpose {
78    fn default() -> Self {
79        Self::Random
80    }
81}
82
83/*
84# Conversation
85This is used to generate conversational content for NPCs
86*/
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
88#[cfg_attr(feature = "fltkform", derive(FltkForm))]
89pub enum Conversation {
90    /// General life advice, such as proverbs
91    Advice,
92    /// Describing anything in great detail
93    Details,
94    /// Talks about their own dreams
95    Dreams,
96    /// Talking about an in-gmae event
97    Event,
98    /// Breaking the fourth wall, being self aware
99    Fourth,
100    /// A simple "Hello", "Good day", "How are you?"
101    Greeting,
102    /// General gameplay tips
103    Hint,
104    /// Random puns
105    Jokes,
106    /// Talks about personal problems
107    Problems,
108    /// In game hints to drive the story/quest
109    Quest,
110    /// Random public domain material
111    Quotes,
112    /// Any number of random things about the weather, sports, etc.
113    SmallTalk,
114    /// Talks about geological/biological features of the surroundings
115    Surroundings,
116}
117impl fmt::Display for Conversation {
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        let v:String;
120        match *self {
121            Conversation::Quest => v = String::from("Quest"),
122            Conversation::Hint => v = String::from("Hint"),
123            Conversation::Advice => v = String::from("Advice"),
124            Conversation::Greeting => v = String::from("Greeting"),
125            Conversation::SmallTalk => v = String::from("SmallTalk"),
126            Conversation::Event => v = String::from("Event"),
127            Conversation::Surroundings => v = String::from("Surroundings"),
128            Conversation::Problems => v = String::from("Problems"),
129            Conversation::Dreams => v = String::from("Dreams"),
130            Conversation::Jokes => v = String::from("Jokes"),
131            Conversation::Quotes => v = String::from("Quotes"),
132            Conversation::Details => v = String::from("Details"),
133            Conversation::Fourth => v = String::from("Fourth"),
134
135        }
136        write!(f, "{}", v.as_str())
137    }
138}
139impl Default for Conversation {
140    fn default() -> Self {
141        Self::SmallTalk
142    }
143}
144
145/*
146*/
147#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
148#[cfg_attr(feature = "fltkform", derive(FltkForm))]
149pub struct Script {
150    /// The list of lines
151    pub lines:Vec<u32>,
152    /// Type of conversation
153    pub conversation:Conversation,
154    /// The file to read the text from
155    pub file:String,
156    /// Are the lines to be ready randomly or in order?
157    pub random:bool,
158    /// Are we done here?
159    pub finished:bool
160}
161impl Random for Script{
162    type Type = Script;
163    fn random_type(&self) -> Self::Type {
164        Self::empty()
165    }
166}
167impl Script {
168    /// Make a generic conversation script
169    #[allow(dead_code)]
170    pub fn empty() -> Self where Self:Sized {
171        Script {
172            lines:vec![],
173            conversation:Conversation::SmallTalk,
174            file:String::from(""),
175            random:true,
176            finished:false,
177        }
178    }
179    #[allow(dead_code)]
180    pub fn default_words(&self)-> String {
181        if self.half() {
182            return String::from("Math is way more exciting than people really think it is...")
183        }
184        String::from("Aren't role playing games fascinating!")
185    }
186    #[allow(dead_code)]
187    pub fn speak(&self) -> Option<String> {
188        if self.finished {
189            return None
190        }
191        if self.file != *"" {
192            if !self.random {
193                // look at lines
194                // look at conversation type
195                
196            }
197            //TODO
198            return Some(self.default_words())
199        }
200        Some(self.default_words())
201    }
202}
203
204/*
205# NPC
206This holds our Non-Player Characters
207*/
208#[allow(clippy::upper_case_acronyms)]
209#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
210#[cfg_attr(feature = "fltkform", derive(FltkForm))]
211pub struct NPC {
212    /// Whether the NPC is on task
213    pub state:State,
214    /// Are they a Random, a Story, or an NPC with a specific Task, like a merchant?
215    pub purpose:Purpose,
216    /// The first time you speak **only**
217    pub meet:Conversation, // Script
218    /// Every other time
219    pub conversations:Vec<Conversation>, // Vec<Script>
220}
221impl NPC {
222
223    /// Make an empty conversation NPC
224    #[allow(dead_code)]
225    pub fn empty() -> Self where Self:Sized {
226        NPC {
227            state:State::Broken,
228            purpose:Purpose::Random,
229            meet:Conversation::Greeting,
230            conversations:vec!{},
231        }
232    }
233
234    /// Make a generic conversation NPC
235    pub fn basic() -> Self where Self:Sized {
236        let conversations:Vec<Conversation> = vec![ Conversation::SmallTalk, Conversation::Surroundings,  Conversation::Quotes, Conversation::Details, Conversation::Advice ];
237        NPC {
238            state:State::Ordered,
239            purpose:Purpose::Random,
240            meet:Conversation::Greeting,
241            conversations,
242        }
243    }
244}
245impl Default for NPC {
246    fn default() -> Self where Self:Sized {
247        Self::basic()
248    }
249}