/// <summary> /// Update the set of recruits /// </summary> internal void CreateRecruits() { var recuritsToRemove = new List <string>(); //remove recruits from iat and randomly select to remove them from pool of available recruits foreach (var member in Recruits) { var path = Path.Combine(storageLocation, member.Value.RolePlayCharacter.VoiceName + ".rpc").Replace("\\", "/"); iat.RemoveCharacters(new List <int> { iat.GetAllCharacterSources().First(c => c.Source.Replace("\\", "/") == path).Id }); if (StaticRandom.Int(0, 100) % ConfigKey.RecruitChangeChance.GetIntValue() != 0) { recuritsToRemove.Add(member.Key); } } foreach (var member in recuritsToRemove) { Recruits.Remove(member); } //for the amount of empty recruit spaces, create a new recruit var amount = ConfigKey.RecruitCount.GetIntValue() - Recruits.Count; for (var i = 0; i < amount; i++) { var position = Boat.GetWeakestPosition(CrewMembers.Values.Concat(Recruits.Values).ToList()); var newMember = new CrewMember(position, Nationality); UniqueNameCheck(newMember); Recruits.Add(newMember.Name, newMember); } var storeNum = 0; //set up the files for each recruit and their avatar foreach (var recruit in Recruits) { recruit.Value.CreateRecruitFile(iat, storageLocation, storeNum); storeNum++; } iat.Save(); }
/// <summary> /// Create opinions for everyone included in the list for this CrewMember /// </summary> internal void CreateInitialOpinions(List <string> people, bool save = false) { foreach (var person in people) { if (person == Name) { continue; } AddOrUpdateOpinion(person, StaticRandom.Int(ConfigKey.DefaultOpinionMin.GetIntValue(), ConfigKey.DefaultOpinionMax.GetIntValue() + 1)); //if the two people share the same last name, give the bonus stated in the config to their opinion if (person.GetType() == typeof(CrewMember) && LastName == person.Split(new[] { ' ' }, 2).Last()) { AddOrUpdateOpinion(person, ConfigKey.LastNameBonusOpinion.GetIntValue()); } AddOrUpdateRevealedOpinion(person, 0, false); } if (save) { SaveStatus(); } }
/// <summary> /// Randomly select the gender of the CrewMember /// </summary> private string SelectGender() { return(StaticRandom.Int(0, 1000) % 2 == 0 ? "M" : "F"); }
/// <summary> /// Constructor for creating a CrewMember with a random age/gender/name /// </summary> internal CrewMember(Position position, string nationality) : this() { Gender = SelectGender(); Age = StaticRandom.Int(18, 45); Nationality = nationality; SelectRandomName(); foreach (Skill skill in Enum.GetValues(typeof(Skill))) { if (position != Position.Null) { Skills[skill] = position.RequiresSkill(skill) ? StaticRandom.Int(ConfigKey.GoodPositionRating.GetIntValue(), 11) : StaticRandom.Int(1, ConfigKey.BadPositionRating.GetIntValue() + 1); } else { Skills[skill] = StaticRandom.Int(ConfigKey.RandomSkillLow.GetIntValue(), ConfigKey.RandomSkillHigh.GetIntValue() + 1); } } }
/// <summary> /// Make changes based off of post-race events /// </summary> private void PostRaceFeedback(string ev, Team team, List <string> subjects) { var spacelessName = Name.NoSpaces(); var eventString = EventHelper.ActionStart("Player", $"PostRace({ev})", spacelessName); if (ev.Contains("(")) { eventString = EventHelper.ActionStart("Player", ev, spacelessName); } //event perceived to trigger any mood change events kept in the EA file RolePlayCharacter.Perceive(eventString); if (Enum.IsDefined(typeof(PostRaceEventImpact), ev)) { //trigger different changes based off of what dialogue the player last picked switch ((PostRaceEventImpact)Enum.Parse(typeof(PostRaceEventImpact), ev)) { //improve opinion of manager, crew member now expects to be picked in the selected position (subjects[0]) next race case PostRaceEventImpact.ExpectedPosition: AddOrUpdateOpinion(team.ManagerName, 1); UpdateSingleBelief(NPCBelief.ExpectedPosition, subjects[0]); break; //improve opinion of manager, crew member now expects to be picked in the selected position (subjects[0]) in two races time case PostRaceEventImpact.ExpectedPositionAfter: AddOrUpdateOpinion(team.ManagerName, 1); UpdateSingleBelief(NPCBelief.ExpectedPositionAfter, subjects[0]); break; //make opinion of manager worse case PostRaceEventImpact.ManagerOpinionWorse: AddOrUpdateOpinion(team.ManagerName, -1); break; //make all crew members' opinion of manager worse case PostRaceEventImpact.ManagerOpinionAllCrewWorse: foreach (var cm in team.CrewMembers) { cm.Value.AddOrUpdateOpinion(team.ManagerName, -2); cm.Value.SaveStatus(); } break; //improve opinion of manager case PostRaceEventImpact.ManagerOpinionBetter: AddOrUpdateOpinion(team.ManagerName, 1); break; //make all crew members' opinion of manager better case PostRaceEventImpact.ManagerOpinionAllCrewBetter: foreach (var cm in team.CrewMembers) { cm.Value.AddOrUpdateOpinion(team.ManagerName, 2); cm.Value.SaveStatus(); } break; //improve opinion of manager greatly case PostRaceEventImpact.ManagerOpinionMuchBetter: AddOrUpdateOpinion(team.ManagerName, 5); break; //make opinion of manager much worse case PostRaceEventImpact.ManagerOpinionMuchWorse: AddOrUpdateOpinion(team.ManagerName, -5); break; //reveal two random skills for this crew member (can be already revealed skills) case PostRaceEventImpact.RevealTwoSkills: AddOrUpdateOpinion(team.ManagerName, 1); for (var i = 0; i < 2; i++) { var randomStat = Math.Pow(2, StaticRandom.Int(0, Skills.Count)); var statName = ((Skill)randomStat).ToString(); var statValue = Skills[(Skill)randomStat]; RevealedSkills[(Skill)randomStat] = statValue; UpdateSingleBelief(NPCBelief.RevealedSkill, statValue, statName); } break; //reveal four random skills for this crew member (can be already revealed skills) case PostRaceEventImpact.RevealFourSkills: AddOrUpdateOpinion(team.ManagerName, 3); for (var i = 0; i < 4; i++) { var randomStat = Math.Pow(2, StaticRandom.Int(0, Skills.Count)); var statName = ((Skill)randomStat).ToString(); var statValue = Skills[(Skill)randomStat]; RevealedSkills[(Skill)randomStat] = statValue; UpdateSingleBelief(NPCBelief.RevealedSkill, statValue, statName); } break; //improve all crew members' opinion of the crew member who was the subject of the event (subjects[0]) greatly and reveals their opinion. //Regex adds spaces back before each capital letter case PostRaceEventImpact.ImproveConflictOpinionGreatly: var subGreatHelp = Regex.Replace(subjects[0], @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0"); foreach (var cm in team.CrewMembers) { if (cm.Key != subGreatHelp) { cm.Value.AddOrUpdateOpinion(subGreatHelp, 2); cm.Value.AddOrUpdateRevealedOpinion(subGreatHelp, cm.Value.CrewOpinions[subGreatHelp]); cm.Value.SaveStatus(); } } break; //improve all crew members' opinion of the crew member who was the subject of the event (subjects[0]) and reveals their opinion. //Regex adds spaces back before each capital letter case PostRaceEventImpact.ImproveConflictTeamOpinion: var subHelp = Regex.Replace(subjects[0], @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0"); foreach (var cm in team.CrewMembers) { if (cm.Key != subHelp) { cm.Value.AddOrUpdateOpinion(subHelp, 1); cm.Value.AddOrUpdateRevealedOpinion(subHelp, cm.Value.CrewOpinions[subHelp]); cm.Value.SaveStatus(); } } break; //reveals all crew members' opinion of the crew member who was the subject of the event (subjects[0]) and slightly improves this the opinion of the manager for this crew member. //Regex adds spaces back before each capital letter case PostRaceEventImpact.ImproveConflictKnowledge: var subKnow = Regex.Replace(subjects[0], @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0"); AddOrUpdateOpinion(team.ManagerName, 1); foreach (var cm in team.CrewMembers) { if (cm.Key != subKnow) { cm.Value.AddOrUpdateRevealedOpinion(subKnow, cm.Value.CrewOpinions[subKnow]); cm.Value.SaveStatus(); } } break; //improve opinion of manager, expects to be placed in perferred position (subjects[0]) next race //other crew member involved in this event (subjects[1]) - improve opinion of manager, expects to be placed in perferred position (subjects[0]) in two races times //Regex adds spaces back before each capital letter case PostRaceEventImpact.CausesSelectionAfter: AddOrUpdateOpinion(team.ManagerName, 1); UpdateSingleBelief(NPCBelief.ExpectedPosition, subjects[0]); var otherPlayer = Regex.Replace(subjects[1], @"((?<=\p{Ll})\p{Lu})|((?!\A)\p{Lu}(?>\p{Ll}))", " $0"); team.CrewMembers[otherPlayer].AddOrUpdateOpinion(team.ManagerName, 1); team.CrewMembers[otherPlayer].UpdateSingleBelief(NPCBelief.ExpectedPositionAfter, subjects[0]); team.CrewMembers[otherPlayer].SaveStatus(); break; //improves opinion of manager greatly, all unselected crew members' opinion of manager improves and expect to be selected next race case PostRaceEventImpact.WholeTeamChange: AddOrUpdateOpinion(team.ManagerName, 4); foreach (var cm in team.CrewMembers) { if (!team.PreviousSession.PositionCrew.Values.Select(v => v.Name).Contains(cm.Key)) { cm.Value.AddOrUpdateOpinion(team.ManagerName, 1); cm.Value.UpdateSingleBelief(NPCBelief.ExpectedSelection, "true"); cm.Value.SaveStatus(); } } break; } } TickUpdate(0); }
/// <summary> /// Send an event to the EA/RPC to get CrewMember information /// </summary> internal List <string> SendMeetingEvent(IntegratedAuthoringToolAsset iat, string style, Team team) { var reply = new List <string>(); switch (style) { case "StatReveal": //select a random skill that has not been displayed before or any random skill if all have been displayed var availableStats = RevealedSkills.Where(s => s.Value == 0).Select(s => s.Key).ToList(); if (availableStats.Count == 0) { availableStats = RevealedSkills.Where(s => s.Value != Skills[s.Key]).Select(s => s.Key).ToList(); } if (availableStats.Count == 0) { availableStats = RevealedSkills.Select(s => s.Key).ToList(); } var randomStat = StaticRandom.Int(0, availableStats.Count); var statName = availableStats[randomStat].ToString(); var selectedStat = (Skill)Enum.Parse(typeof(Skill), statName); var statValue = Skills[selectedStat]; //add this skill rating to the dictionary to revealed skills RevealedSkills[selectedStat] = statValue; //get available dialogue based off of the rating in the skill style += statValue <= ConfigKey.BadSkillRating.GetIntValue() ? "Bad" : statValue >= ConfigKey.GoodSkillRating.GetIntValue() ? "Good" : "Middle"; reply.Add(statName.ToLower()); //save that this skill has been revealed UpdateSingleBelief(NPCBelief.RevealedSkill, statValue, statName); break; case "RoleReveal": //select a random position var pos = team.Boat.Positions[StaticRandom.Int(0, team.Boat.PositionCount)]; //get dialogue based on if they would be above or below mid-range in this position style += pos.GetPositionRating(this) <= 5 ? "Bad" : "Good"; reply.Add(pos.ToString()); break; case "OpinionRevealPositive": //get all opinions for active crewmembers and the manager var crewOpinionsPositive = CrewOpinions.Where(c => team.CrewMembers.ContainsKey(c.Key)).ToDictionary(p => p.Key, p => p.Value); crewOpinionsPositive.Add(team.ManagerName, CrewOpinions[team.ManagerName]); //get all opinions where the value is equal/greater than the OpinionLike value in the config var opinionsPositive = crewOpinionsPositive.Where(co => co.Value >= ConfigKey.OpinionLike.GetIntValue()).ToDictionary(o => o.Key, o => o.Value); //if there are any positive opinions if (opinionsPositive.Any()) { //select an opinion at random var pickedOpinionPositive = opinionsPositive.OrderBy(o => Guid.NewGuid()).First(); if (pickedOpinionPositive.Value >= ConfigKey.OpinionStrongLike.GetIntValue()) { style += "High"; } reply.Add(pickedOpinionPositive.Key != team.ManagerName ? pickedOpinionPositive.Key : "you"); AddOrUpdateRevealedOpinion(pickedOpinionPositive.Key, pickedOpinionPositive.Value); } //if there are no positive opinions, get dialogue based on that else { style += "None"; } break; case "OpinionRevealNegative": var crewOpinionsNegative = CrewOpinions.Where(c => team.CrewMembers.ContainsKey(c.Key)).ToDictionary(p => p.Key, p => p.Value); crewOpinionsNegative.Add(team.ManagerName, CrewOpinions[team.ManagerName]); var opinionsNegative = crewOpinionsNegative.Where(co => co.Value <= ConfigKey.OpinionDislike.GetIntValue()).ToDictionary(o => o.Key, o => o.Value); if (opinionsNegative.Any()) { var pickedOpinionNegative = opinionsNegative.OrderBy(o => Guid.NewGuid()).First(); if (pickedOpinionNegative.Value >= ConfigKey.OpinionStrongDislike.GetIntValue()) { style += "High"; } reply.Add(pickedOpinionNegative.Key != team.ManagerName ? pickedOpinionNegative.Key : "you"); AddOrUpdateRevealedOpinion(pickedOpinionNegative.Key, pickedOpinionNegative.Value); } else { style += "None"; } break; } SaveStatus(); var dialogueOptions = iat.GetDialogueActionsByState("NPC_" + style).ToList(); if (dialogueOptions.Any()) { reply.Insert(0, dialogueOptions.OrderBy(o => Guid.NewGuid()).First().Utterance); return(reply); } reply.Clear(); return(reply); }
/// <summary> /// Select event(s) to trigger post race /// </summary> internal void SelectPostRaceEvents(Team team, int chance) { //get the state of currently running events foreach (var crewMember in team.CrewMembers.Values) { crewMember.CurrentEventCheck(team, iat); } var selectedEvents = new List <List <PostRaceEventState> >(); //get all possible post-race event starting dialogue var dialogueOptions = GetPossiblePostRaceDialogue(); //get events that can be fired according to the game config var events = GetEvents(dialogueOptions, team); if (events.Any()) { var allCrewInitial = team.CrewMembers; var allCrew = new Dictionary <string, CrewMember>(); //remove those already involved in a running event to not be selected foreach (var crewMember in allCrewInitial.Values) { var expectsPos = crewMember.LoadBelief(NPCBelief.ExpectedPosition); var expectsPosAfter = crewMember.LoadBelief(NPCBelief.ExpectedPosition); var expectsSelection = crewMember.LoadBelief(NPCBelief.ExpectedSelection); if (expectsPos == null && expectsPosAfter == null && expectsSelection == null) { allCrew.Add(crewMember.Name, crewMember); } } foreach (var ev in events) { var eventCrew = new Dictionary <string, CrewMember>(allCrew); //if there is no-one to select for an event, stop looking if (eventCrew.Count == 0) { continue; } var selected = dialogueOptions.Where(a => a.NextState == "Player_" + ev.EventName).OrderBy(o => Guid.NewGuid()).First(); if (ev.Random && StaticRandom.Int(0, (int)Math.Pow(chance, selectedEvents.Count + 1)) != 0) { continue; } var eventSelected = new List <PostRaceEventState>(); switch (selected.NextState.Replace("Player_", string.Empty)) { case "PW": //for this event, select a crew member who feels that they were placed in the wrong position var betterPlace = new List <KeyValuePair <CrewMember, Position> >(); foreach (var pair in team.PreviousSession.PositionCrew) { var betterPosition = CrewMemberBestPosition(pair.Value, team); if (betterPosition.Key != Position.Null) { betterPlace.Add(new KeyValuePair <CrewMember, Position>(pair.Value, betterPosition.Key)); } eventCrew.Remove(pair.Value.Name); } //if nobody currently placed can be put into a better position, select a placed crew member and position at random if (!betterPlace.Any()) { var selectedCrewMember = eventCrew.OrderBy(c => Guid.NewGuid()).First().Value; betterPlace.Add(new KeyValuePair <CrewMember, Position>(selectedCrewMember, CrewMemberBestPosition(selectedCrewMember, team).Key)); } betterPlace = betterPlace.OrderBy(c => Guid.NewGuid()).ToList(); eventSelected.Add(new PostRaceEventState(betterPlace.First().Key, selected, new[] { betterPlace.First().Value.ToString() }.ToList())); break; case "OO": //for this event, select a crew member who is placed with someone they do not get on with var selectedAgainst = team.CrewMembers.OrderByDescending(c => c.Value.RevealedSkills.Values.Sum()).First(); if (eventCrew.ContainsKey(selectedAgainst.Key)) { eventCrew.Remove(selectedAgainst.Key); if (eventCrew.Count == 0) { continue; } } var selectedFor = eventCrew.OrderBy(c => Guid.NewGuid()).First(); //make it so selectedFor has the worst possible opinion of selectedAgainst selectedFor.Value.AddOrUpdateOpinion(selectedAgainst.Key, -10); selectedFor.Value.AddOrUpdateRevealedOpinion(selectedAgainst.Key, selectedFor.Value.CrewOpinions[selectedAgainst.Key]); selectedFor.Value.SaveStatus(); //randomly adjusted everyone else's opinion of selectedAgainst foreach (var cm in team.CrewMembers) { if (cm.Key != selectedFor.Key && cm.Key != selectedAgainst.Key) { cm.Value.AddOrUpdateOpinion(selectedAgainst.Key, StaticRandom.Int(-3, 1)); cm.Value.SaveStatus(); } } eventSelected.Add(new PostRaceEventState(selectedFor.Value, selected, new[] { selectedAgainst.Key.NoSpaces() }.ToList())); break; case "NotPicked": //for this event, select a crew member who was not selected foreach (var pair in team.PreviousSession.PositionCrew) { eventCrew.Remove(pair.Value.Name); } if (eventCrew.Count == 0) { continue; } var randomCrewMember = eventCrew.OrderBy(c => Guid.NewGuid()).First(); var randomBestPosition = CrewMemberBestPosition(randomCrewMember.Value, team); var randomPositionCurrent = team.PreviousSession.PositionCrew[randomBestPosition.Key].Name; eventSelected.Add(new PostRaceEventState(randomCrewMember.Value, selected, new[] { randomBestPosition.Key.ToString(), randomPositionCurrent.NoSpaces() }.ToList())); break; case "IPC": //for this event, select a crew member to have a conflict with the skipper if (!team.PreviousSession.PositionCrew.ContainsKey(Position.Skipper)) { continue; } var skipper = team.PreviousSession.PositionCrew[Position.Skipper].Name; if (eventCrew.ContainsKey(skipper)) { eventCrew.Remove(skipper); } if (eventCrew.Count == 0) { continue; } var randomAdditional = eventCrew.OrderBy(c => Guid.NewGuid()).First(); eventCrew.Remove(randomAdditional.Key); if (eventCrew.Count == 0) { continue; } eventSelected.Add(new PostRaceEventState(eventCrew.OrderBy(c => Guid.NewGuid()).First().Value, selected, new[] { skipper.NoSpaces(), randomAdditional.Key.NoSpaces() }.ToList())); break; } eventSelected.ForEach(es => allCrew.Remove(es.CrewMember.Name)); selectedEvents.Add(eventSelected); } } PostRaceEvents = selectedEvents; SaveEvents(team.Manager); }
/// <summary> /// Create a new Avatar based on randomness and this Crew Member's best skill or load the existing Avatar for this Crew Member /// </summary> private void CreateAvatar(CrewMember crewMember) { //Get Best Skill var currentBestSkill = crewMember.LoadBelief(NPCBelief.AvatarBestSkill); _bestSkill = Enum.TryParse <Skill>(currentBestSkill, out var loadedBestSkill) ? loadedBestSkill : GetBestSkill(crewMember); _bodyType = GetBodyType(); //Set Skin Color var loadedMouthColor = crewMember.LoadBelief(NPCBelief.AvatarMouthColor); if (loadedMouthColor != null && byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarSkinColorRed), out var skinColorRed) && byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarSkinColorGreen), out var skinColorGreen) && byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarSkinColorBlue), out var skinColorBlue)) { SkinColor = new Color(skinColorRed, skinColorGreen, skinColorBlue); MouthColor = loadedMouthColor; } else { SkinColor = GetRandomSkinColor(); } //Set Hair Color if (byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarHairColorRed), out var hairColorRed) && byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarHairColorGreen), out var hairColorGreen) && byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarHairColorBlue), out var hairColorBlue)) { HairColor = new Color(hairColorRed, hairColorGreen, hairColorBlue); } else { HairColor = Config.RandomHairColor ? GetRandomHairColor() : GetHairColorForSkin(SkinColor); } //Set Body Type BodyType = crewMember.LoadBelief(NPCBelief.AvatarBodyType) ?? $"Body{_gender}_{_bodyType}"; //Set Hair Type HairType = crewMember.LoadBelief(NPCBelief.AvatarHairType) ?? $"Hair{StaticRandom.Int(1, Config.HairTypesCount + 1):00}{_gender}"; //Set Eye Type EyeType = crewMember.LoadBelief(NPCBelief.AvatarEyeType) ?? $"Eye{_gender}_{_bestSkill}"; //Set Eye Color var textEyeColor = crewMember.LoadBelief(NPCBelief.AvatarEyeColor); if (textEyeColor != null) { EyeColor = GetEyeColorFromText(textEyeColor); } else { if (byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarEyeColorRed), out var eyeColorRed) && byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarEyeColorGreen), out var eyeColorGreen) && byte.TryParse(crewMember.LoadBelief(NPCBelief.AvatarEyeColorBlue), out var eyeColorBlue)) { EyeColor = new Color(eyeColorRed, eyeColorGreen, eyeColorBlue); } else { EyeColor = GetRandomEyeColor(); } } //Set Face type EyebrowType = crewMember.LoadBelief(NPCBelief.AvatarEyebrowType) ?? $"Face{_gender}_{_bestSkill}_Eyebrows"; NoseType = crewMember.LoadBelief(NPCBelief.AvatarNoseType) ?? $"Face{_gender}_{_bestSkill}_Nose"; //Specify the teeth for male avatars if (IsMale) { TeethType = crewMember.LoadBelief(NPCBelief.AvatarTeethType) ?? $"Face{_gender}_{_bestSkill}_Teeth"; } //Set Mouth Type MouthType = crewMember.LoadBelief(NPCBelief.AvatarMouthType) ?? $"Face{_gender}_{_bestSkill}_Mouth"; // Set Height and Width (Weight) if (float.TryParse(crewMember.LoadBelief(NPCBelief.AvatarHeight), out var loadedHeight)) { Height = loadedHeight; } else { Height = 1 + StaticRandom.Float(-0.075f, 0.075f); } if (float.TryParse(crewMember.LoadBelief(NPCBelief.AvatarWeight), out var loadedWeight)) { Weight = loadedWeight; } else { Weight = 1 + StaticRandom.Float(-0.075f, 0.075f); } //Save attributes UpdateAvatarBeliefs(crewMember); }