@@ -48,74 +48,118 @@ internal static string HandleResearchCommand(ChatMessageWrapper messageWrapper,
4848 private static string GetCurrentResearchStatus ( )
4949 {
5050 var researchManager = Find . ResearchManager ;
51- var currentProject = researchManager . GetProject ( ) ; // note: in 1.5 this is .CurrentProject
51+ var currentProject = researchManager . GetProject ( ) ; // or .CurrentProject if 1.5+
5252
5353 if ( currentProject == null )
5454 {
5555 return "RICS.Research.NoArgsCurrent" . Translate ( ) ;
5656 }
5757
58- // Use defensive defaults
5958 float progress = Math . Max ( 0f , currentProject . ProgressApparent ) ;
60- float cost = Math . Max ( 1f , currentProject . CostApparent ) ; // prevent div-by-zero
59+ float cost = Math . Max ( 1f , currentProject . CostApparent ) ;
6160
62- // Prevent NaN / div-by-zero / empty formatting
6361 if ( float . IsNaN ( progress ) || float . IsInfinity ( progress ) ) progress = 0f ;
6462 if ( float . IsNaN ( cost ) || float . IsInfinity ( cost ) ) cost = 0f ;
65- if ( cost <= 0f ) cost = 1f ; // avoid div-by-zero in percent
6663
6764 float percent = ( progress / cost ) * 100f ;
6865
69- // Optional: show at least "0" even if values are tiny
66+ // Log as before
67+ Logger . Debug ( $ "Current: { currentProject . LabelCap } - raw progress { currentProject . ProgressApparent } / { currentProject . CostApparent } → clamped { progress } /{ cost } → { percent : F1} %") ;
68+
69+ // Format numbers manually with .ToString("F0") or "0"
70+ string progStr = progress . ToString ( "F0" ) ; // or just progress > 0.1f ? progress.ToString("F0") : "0"
71+ string costStr = cost . ToString ( "F0" ) ;
72+ string percStr = percent . ToString ( "F1" ) ;
73+
7074 return "RICS.Research.CurrentStatus" . Translate (
7175 currentProject . LabelCap ,
72- progress > 0.001f ? progress : 0f ,
73- cost > 0.001f ? cost : 0f ,
74- percent >= 0f ? percent : 0f
76+ progStr ,
77+ costStr ,
78+ percStr
7579 ) ;
7680 }
7781
7882 private static string GetSpecificResearchStatus ( string researchName )
7983 {
8084 var allResearch = DefDatabase < ResearchProjectDef > . AllDefs ;
81- var matchingProjects = allResearch . Where ( r =>
82- r . LabelCap . ToString ( ) . ToLower ( ) . Contains ( researchName . ToLower ( ) ) ||
83- r . defName . ToLower ( ) . Contains ( researchName . ToLower ( ) )
84- ) . ToList ( ) ;
85+ string inputLower = researchName . ToLower ( ) . Trim ( ) ;
8586
86- if ( matchingProjects . Count == 0 )
87+ // 1. Try exact matches first (case-insensitive)
88+ var exactMatches = allResearch
89+ . Where ( r =>
90+ string . Equals ( r . LabelCap . ToString ( ) , inputLower , StringComparison . OrdinalIgnoreCase ) ||
91+ string . Equals ( r . defName , inputLower , StringComparison . OrdinalIgnoreCase ) )
92+ . ToList ( ) ;
93+
94+ if ( exactMatches . Count == 1 )
95+ {
96+ return GetProjectStatusString ( exactMatches [ 0 ] ) ;
97+ }
98+
99+ if ( exactMatches . Count > 1 )
100+ {
101+ var names = string . Join ( ", " , exactMatches . Select ( p => p . LabelCap ) ) ;
102+ return $ "Multiple exact matches for '{ researchName } ': { names } ";
103+ }
104+
105+ // 2. No exact → fall back to partial contains
106+ var partialMatches = allResearch
107+ . Where ( r =>
108+ r . LabelCap . ToString ( ) . ToLower ( ) . Contains ( inputLower ) ||
109+ r . defName . ToLower ( ) . Contains ( inputLower ) )
110+ . ToList ( ) ;
111+
112+ if ( partialMatches . Count == 0 )
87113 {
88114 return "RICS.Research.NoMatch" . Translate ( researchName ) ;
89115 }
90116
91- if ( matchingProjects . Count > 1 )
117+ if ( partialMatches . Count > 1 )
92118 {
93- var projectNames = string . Join ( ", " , matchingProjects . Take ( 3 ) . Select ( p => p . LabelCap ) ) ;
94- string ellipsis = matchingProjects . Count > 3 ? "RICS.Research.MultipleEllipsis" . Translate ( ) : "" ;
95- return "RICS.Research.MultipleMatches" . Translate ( researchName , projectNames , ellipsis ) ;
119+ var names = string . Join ( ", " , partialMatches . Take ( 3 ) . Select ( p => p . LabelCap ) ) ;
120+ string ellipsis = partialMatches . Count > 3 ? "RICS.Research.MultipleEllipsis" . Translate ( ) : "" ;
121+ return "RICS.Research.MultipleMatches" . Translate ( researchName , names , ellipsis ) ;
96122 }
97123
98- var project = matchingProjects [ 0 ] ;
124+ // Single partial match
125+ return GetProjectStatusString ( partialMatches [ 0 ] ) ;
126+ }
127+
128+ private static string GetProjectStatusString ( ResearchProjectDef project )
129+ {
99130 var researchManager = Find . ResearchManager ;
100131
101132 if ( project . IsFinished )
102133 {
103134 return "RICS.Research.Completed" . Translate ( project . LabelCap ) ;
104135 }
105136
106- float progress = researchManager . GetProgress ( project ) ;
107- float totalCost = project . CostApparent ;
108- float percent = totalCost > 0 ? ( progress / totalCost ) * 100 : 0 ;
137+ // Defensive handling
138+ float rawProgress = project . ProgressApparent ;
139+ float rawCost = project . CostApparent ;
140+
141+ float progress = Math . Max ( 0f , rawProgress ) ;
142+ float cost = Math . Max ( 1f , rawCost ) ;
143+
144+ if ( float . IsNaN ( progress ) || float . IsInfinity ( progress ) ) progress = 0f ;
145+ if ( float . IsNaN ( cost ) || float . IsInfinity ( cost ) ) cost = 1f ;
146+
147+ float percent = ( progress / cost ) * 100f ;
148+
149+ // Pre-format as strings (same as current research)
150+ string progStr = progress . ToString ( "F0" ) ;
151+ string costStr = cost . ToString ( "F0" ) ;
152+ string percStr = percent . ToString ( "F1" ) ; // keeps one decimal like 5.6
109153
110154 string status = project . CanStartNow
111155 ? "RICS.Research.StatusAvailable" . Translate ( )
112156 : "RICS.Research.StatusLocked" . Translate ( ) ;
113157
114158 return "RICS.Research.SpecificStatus" . Translate (
115159 project . LabelCap ,
116- progress ,
117- totalCost ,
118- percent ,
160+ progStr ,
161+ costStr ,
162+ percStr ,
119163 status
120164 ) ;
121165 }
@@ -141,22 +185,41 @@ internal static string HandleStudyCommand(ChatMessageWrapper messageWrapper, str
141185 . FirstOrDefault ( p => p . knowledgeCategory . overflowCategory != null ) ;
142186
143187 string bas = basic != null
144- ? "RICS.Research.StudyFormat" . Translate (
145- basic . LabelCap ,
146- basic . ProgressApparent ,
147- basic . CostApparent ,
148- basic . ProgressPercent )
188+ ? FormatStudyProject ( basic )
149189 : "RICS.Research.StudyNone" . Translate ( ) ;
150190
151191 string adv = advanced != null
152- ? "RICS.Research.StudyFormat" . Translate (
153- advanced . LabelCap ,
154- advanced . ProgressApparent ,
155- advanced . CostApparent ,
156- advanced . ProgressPercent )
192+ ? FormatStudyProject ( advanced )
157193 : "RICS.Research.StudyNone" . Translate ( ) ;
158194
159195 return "RICS.Research.StudyStatus" . Translate ( bas , adv ) ;
160196 }
197+
198+ // New helper to avoid duplication and keep formatting consistent
199+ private static string FormatStudyProject ( ResearchProjectDef project )
200+ {
201+ // Use same defensive logic
202+ float rawProg = project . ProgressApparent ;
203+ float rawCost = project . CostApparent ;
204+
205+ float progress = Math . Max ( 0f , rawProg ) ;
206+ float cost = Math . Max ( 1f , rawCost ) ;
207+
208+ if ( float . IsNaN ( progress ) || float . IsInfinity ( progress ) ) progress = 0f ;
209+ if ( float . IsNaN ( cost ) || float . IsInfinity ( cost ) ) cost = 1f ;
210+
211+ float percent = ( progress / cost ) * 100f ;
212+
213+ string progStr = progress . ToString ( "F0" ) ;
214+ string costStr = cost . ToString ( "F0" ) ;
215+ string percStr = percent . ToString ( "F1" ) ;
216+
217+ return "RICS.Research.StudyFormat" . Translate (
218+ project . LabelCap ,
219+ progStr ,
220+ costStr ,
221+ percStr
222+ ) ;
223+ }
161224 }
162225}
0 commit comments