1 /*
2 * Seven Kingdoms: Ancient Adversaries
3 *
4 * Copyright 1997,1998 Enlight Software Ltd.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 //Filename : OTALKRES.CPP
22 //Description : Object Talk
23
24 #include <OMOUSE.h>
25 #include <OSYS.h>
26 #include <OVGA.h>
27 #include <OINFO.h>
28 #include <OFONT.h>
29 #include <OTECHRES.h>
30 #include <OGAMESET.h>
31 #include <ONEWS.h>
32 #include <ONATION.h>
33 #include <OTALKRES.h>
34 #include <OREMOTE.h>
35 // #### begin Gilbert 9/10 ######//
36 #include <OSE.h>
37 // #### end Gilbert 9/10 ######//
38 #include "gettext.h"
39
40 //---------- the subtitle area ----------//
41
42 enum { TALK_X1 = ZOOM_X1+16,
43 TALK_Y1 = ZOOM_Y1+252,
44 TALK_X2 = ZOOM_X2-16,
45 TALK_Y2 = ZOOM_Y2-86,
46 };
47
48 enum { TALK_LINE_HEIGHT = 18 };
49
50 //------- define constant --------//
51
52 #define MESSAGE_SENT_STR _("The message has been sent.")
53
54 //------- define static vars -------//
55
56 static String nation_name_str_array[MAX_NATION];
57
58 // ----------- Define static function ----------//
59
60 static char* select_nation_color(char nation_color);
61
62 //---------- Begin of function TalkRes::TalkRes -----------//
63 //
TalkRes()64 TalkRes::TalkRes() : talk_msg_array( sizeof(TalkMsg), 100 )
65 {
66 }
67 //---------- End of function TalkRes::TalkRes -----------//
68
69
70 //---------- Begin of function TalkRes::init -----------//
71 //
init()72 void TalkRes::init()
73 {
74 memset( this, 0, sizeof(TalkRes) - sizeof(talk_msg_array) );
75
76 talk_msg_array.zap();
77 }
78 //---------- End of function TalkRes::init -----------//
79
80
81 //---------- Begin of function TalkRes::deinit -----------//
82 //
deinit()83 void TalkRes::deinit()
84 {
85 }
86 //---------- End of function TalkRes::deinit -----------//
87
88
89 //------- Begin of function TalkRes::init_conversion --------//
90 //
91 // <int> toNationRecno - recno of the nation which the player
92 // is going to talk to.
93 //
init_conversion(int toNationRecno)94 void TalkRes::init_conversion(int toNationRecno)
95 {
96 memset( &cur_talk_msg, 0, sizeof(TalkMsg) );
97
98 cur_talk_msg.from_nation_recno = (char) nation_array.player_recno;
99 cur_talk_msg.to_nation_recno = toNationRecno;
100
101 reply_talk_msg_recno = 0;
102
103 set_talk_choices();
104 }
105 //-------- End of function TalkRes::init_conversion ---------//
106
107
108 //---- Begin of function TalkRes::set_talk_choices ------//
109 //
110 // Set the choices of the next talk.
111 //
112 // return : <int> 1 - choices are set
113 // 0 - if there is no additional talk segment for this talk
114 //
set_talk_choices()115 int TalkRes::set_talk_choices()
116 {
117 //------------------------------------//
118
119 Nation* playerNation = ~nation_array;
120
121 talk_choice_count = 0;
122 choice_question = NULL;
123 choice_question_second_line = NULL;
124 cur_choice_id = 0;
125
126 //------------------------------------//
127
128 int rc=0;
129
130 memset( available_talk_id_array, 0, sizeof(available_talk_id_array) );
131
132 switch( cur_talk_msg.talk_id )
133 {
134 //--- add the main option choices ---//
135
136 case 0:
137 add_main_choices();
138 return 1;
139
140 case TALK_PROPOSE_TRADE_TREATY:
141 case TALK_PROPOSE_FRIENDLY_TREATY:
142 case TALK_PROPOSE_ALLIANCE_TREATY:
143 case TALK_END_TRADE_TREATY:
144 case TALK_END_FRIENDLY_TREATY:
145 case TALK_END_ALLIANCE_TREATY:
146 case TALK_REQUEST_CEASE_WAR:
147 case TALK_DECLARE_WAR:
148 case TALK_REQUEST_MILITARY_AID:
149 return 0;
150
151 case TALK_REQUEST_TRADE_EMBARGO:
152 rc = add_trade_embargo_choices();
153 break;
154
155 case TALK_REQUEST_DECLARE_WAR:
156 rc = add_declare_war_choices();
157 break;
158
159 case TALK_REQUEST_BUY_FOOD:
160 rc = add_buy_food_choices();
161 break;
162
163 case TALK_GIVE_TRIBUTE:
164 case TALK_DEMAND_TRIBUTE:
165 rc = add_tribute_choices();
166 if( rc )
167 choice_question = _("How much tribute?"); // add the choice question here because we use the same function for both tribute and aid
168 break;
169
170 case TALK_GIVE_AID:
171 case TALK_DEMAND_AID:
172 rc = add_tribute_choices();
173 if( rc )
174 choice_question = _("How much aid?");
175 break;
176
177 case TALK_GIVE_TECH:
178 case TALK_DEMAND_TECH:
179 rc = add_give_tech_choices();
180 break;
181
182 case TALK_REQUEST_SURRENDER:
183 rc = add_request_surrender_choices();
184 break;
185
186 case TALK_SURRENDER:
187 rc = add_surrender_choices();
188 break;
189
190 default:
191 err_here();
192 }
193
194 if( rc )
195 add_talk_choice( _("Cancel."), 0 );
196
197 return rc;
198 }
199 //----- End of function TalkRes::set_talk_choices -------//
200
201
202 //---- Begin of function TalkRes::add_main_choices ------//
203 //
add_main_choices()204 void TalkRes::add_main_choices()
205 {
206 static const char* talkMsgArray[] =
207 {
208 N_("Propose a trade treaty."),
209 N_("Propose a friendly treaty."),
210 N_("Propose an alliance treaty."),
211 N_("Terminate our trade treaty."),
212 N_("Terminate our friendly treaty."),
213 N_("Terminate our alliance treaty."),
214 N_("Request immediate military aid."),
215 N_("Request a trade embargo."),
216 N_("Request a cease-fire."),
217 N_("Request a declaration of war against a foe."),
218 N_("Request to purchase food."),
219 N_("Declare war."),
220 N_("Offer to pay tribute."),
221 N_("Demand tribute."),
222 N_("Offer aid."),
223 N_("Request aid."),
224 N_("Offer to transfer technology."),
225 N_("Request technology."),
226 N_("Offer to purchase throne and unite kingdoms."),
227 N_("Surrender."),
228 };
229
230 //-----------------------------------------//
231
232 int rc;
233 int relationStatus = (~nation_array)->get_relation_status(cur_talk_msg.to_nation_recno);
234
235 for( int i=1 ; i<=MAX_TALK_TYPE ; i++ )
236 {
237 if( !can_send_msg(cur_talk_msg.to_nation_recno, nation_array.player_recno, i) )
238 continue;
239
240 rc=0;
241
242 if( !rc )
243 add_talk_choice( _(talkMsgArray[i-1]), i );
244
245 available_talk_id_array[i-1] = 1;
246 }
247 }
248 //----- End of function TalkRes::add_main_choices -------//
249
250
251 //---- Begin of function TalkRes::add_trade_embargo_choices ------//
252 //
add_trade_embargo_choices()253 int TalkRes::add_trade_embargo_choices()
254 {
255 if( cur_talk_msg.talk_para1 )
256 return 0;
257
258 choice_question = _("Request an embargo on trade with which kingdom?");
259
260 Nation* fromNation = nation_array[cur_talk_msg.from_nation_recno];
261 Nation* toNation = nation_array[cur_talk_msg.to_nation_recno];
262
263 for( int i=1 ; i<=nation_array.size() ; i++ )
264 {
265 if( nation_array.is_deleted(i) )
266 continue;
267
268 if( i==cur_talk_msg.from_nation_recno ||
269 i==cur_talk_msg.to_nation_recno )
270 {
271 continue;
272 }
273
274 if( fromNation->get_relation(i)->trade_treaty==0 &&
275 toNation->get_relation(i)->trade_treaty==1 )
276 {
277 //------ add color bar -------//
278
279 nation_name_str_array[i-1] = select_nation_color(nation_array[i]->color_scheme_id);
280
281 //------ add natino name ------//
282
283 nation_name_str_array[i-1] += nation_array[i]->nation_name();
284
285 //---- add talk choice string ------//
286
287 add_talk_choice( nation_name_str_array[i-1], i );
288 }
289 }
290
291 return 1;
292 }
293 //----- End of function TalkRes::add_trade_embargo_choices -------//
294
295
296 //---- Begin of function TalkRes::add_declare_war_choices ------//
297 //
add_declare_war_choices()298 int TalkRes::add_declare_war_choices()
299 {
300 if( cur_talk_msg.talk_para1 )
301 return 0;
302
303 choice_question = _("Declare war on which kingdom?");
304
305 Nation* fromNation = nation_array[cur_talk_msg.from_nation_recno];
306 Nation* toNation = nation_array[cur_talk_msg.to_nation_recno];
307
308 for( int i=1 ; i<=nation_array.size() ; i++ )
309 {
310 if( nation_array.is_deleted(i) )
311 continue;
312
313 //--- can only ask another nation to declare war with a nation that is currently at war with our nation ---//
314
315 if( fromNation->get_relation_status(i) == NATION_HOSTILE &&
316 toNation->get_relation_status(i) != NATION_HOSTILE )
317 {
318 //------ add color bar -------//
319
320 nation_name_str_array[i-1] = select_nation_color(nation_array[i]->color_scheme_id);
321
322 //------ add natino name ------//
323
324 nation_name_str_array[i-1] += nation_array[i]->nation_name();
325
326 //---- add talk choice string ------//
327
328 add_talk_choice( nation_name_str_array[i-1], i );
329 }
330 }
331
332 return 1;
333 }
334 //----- End of function TalkRes::add_declare_war_choices -------//
335
336
337 //---- Begin of function TalkRes::add_buy_food_choices ------//
338 //
add_buy_food_choices()339 int TalkRes::add_buy_food_choices()
340 {
341 #define MIN_FOOD_PURCHASE_PRICE 5
342
343 if( !cur_talk_msg.talk_para1 )
344 {
345 choice_question = _("How much food do you want to purchase?");
346
347 static const char* qtyStrArray[] = { "500.", "1000.", "2000.", "4000." };
348 static short qtyArray[] = { 500, 1000, 2000, 4000 };
349
350 for( int i=0 ; i<4 ; i++ )
351 {
352 if( (~nation_array)->cash >= qtyArray[i] * MIN_FOOD_PURCHASE_PRICE / 10 )
353 add_talk_choice( qtyStrArray[i], qtyArray[i] );
354 }
355
356 return 1;
357 }
358 else if( !cur_talk_msg.talk_para2 )
359 {
360 choice_question = _("How much do you offer for 10 units of food?");
361
362 static const char* priceStrArray[] = { "$5.", "$10.", "$15.", "$20." };
363 static short priceArray[] = { 5, 10, 15, 20 };
364
365 for( int i=0 ; i<4 ; i++ )
366 {
367 if( i==0 || (~nation_array)->cash >= cur_talk_msg.talk_para1 * priceArray[i] / 10 ) // i==0 to at least add one option
368 add_talk_choice( priceStrArray[i], priceArray[i] );
369 }
370
371 return 1;
372 }
373 else
374 return 0;
375 }
376 //----- End of function TalkRes::add_buy_food_choices -------//
377
378
379 //---- Begin of function TalkRes::add_tribute_choices ------//
380 //
add_tribute_choices()381 int TalkRes::add_tribute_choices()
382 {
383 if( cur_talk_msg.talk_para1 )
384 return 0;
385
386 static const char* tributeStrArray[] = { "$500.", "$1000.", "$2000.", "$3000.", "$4000." };
387 static short tributeAmtArray[] = { 500, 1000, 2000, 3000, 4000 };
388
389 for( int i=0 ; i<5 ; i++ )
390 {
391 if( cur_talk_msg.talk_id == TALK_DEMAND_TRIBUTE || // when demand tribute, the amount can be sent to any
392 cur_talk_msg.talk_id == TALK_DEMAND_AID ||
393 (~nation_array)->cash >= tributeAmtArray[i] )
394 {
395 add_talk_choice( tributeStrArray[i], tributeAmtArray[i] );
396 }
397 }
398
399 return 1;
400 }
401 //----- End of function TalkRes::add_tribute_choices -------//
402
403
404 //---- Begin of function TalkRes::add_give_tech_choices ------//
405 //
add_give_tech_choices()406 int TalkRes::add_give_tech_choices()
407 {
408 int i, techNationRecno;
409
410 if( cur_talk_msg.talk_id == TALK_GIVE_TECH )
411 techNationRecno = cur_talk_msg.from_nation_recno;
412 else // demand tech
413 techNationRecno = cur_talk_msg.to_nation_recno;
414
415 if( !cur_talk_msg.talk_para1 )
416 {
417 choice_question = _("Which technology?");
418
419 for( i=1 ; i<=tech_res.tech_count ; i++ )
420 {
421 if( tech_res[i]->get_nation_tech_level(techNationRecno) > 0 )
422 {
423 add_talk_choice( tech_res[i]->tech_des(), i );
424 }
425 }
426
427 return 1;
428 }
429 else if( !cur_talk_msg.talk_para2 && cur_talk_msg.talk_id == TALK_GIVE_TECH )
430 {
431 TechInfo* techInfo = tech_res[cur_talk_msg.talk_para1];
432
433 if( techInfo->max_tech_level==1 ) // this tech only has one level
434 return 0;
435
436 choice_question = _("Which version?");
437
438 int nationLevel = techInfo->get_nation_tech_level(techNationRecno);
439
440 err_when( nationLevel<1 || nationLevel>3 );
441
442 static const char* verStrArray[] = { "Mark I", "Mark II", "Mark III" };
443
444 for( i=1 ; i<=MIN(3, nationLevel) ; i++ )
445 add_talk_choice( verStrArray[i-1], i );
446
447 return 1;
448 }
449 else
450 return 0;
451 }
452 //----- End of function TalkRes::add_give_tech_choices -------//
453
454
455 //---- Begin of function TalkRes::add_request_surrender_choices ------//
456 //
add_request_surrender_choices()457 int TalkRes::add_request_surrender_choices()
458 {
459 if( cur_talk_msg.talk_para1 )
460 return 0;
461
462 choice_question = _("How much do you offer?");
463
464 static const char* strArray[] = { "$5000.", "$7500.", "$10000.",
465 "$15000.", "$20000.", "$30000.", "$40000.", "$50000." };
466
467 static int amtArray[] = { 5000, 7500, 10000, 15000, 20000, 30000, 40000, 50000 };
468
469 for( int i=0 ; i<8 ; i++ )
470 {
471 if( (~nation_array)->cash >= amtArray[i] )
472 {
473 add_talk_choice( strArray[i], amtArray[i]/10 ); // divided by 10 to cope with the limit of <short>
474 }
475 }
476
477 return 1;
478 }
479 //----- End of function TalkRes::add_request_surrender_choices -------//
480
481
482 //---- Begin of function TalkRes::add_surrender_choices ------//
483 //
add_surrender_choices()484 int TalkRes::add_surrender_choices()
485 {
486 if( cur_talk_msg.talk_para1 )
487 return 0;
488
489 static String str;
490
491 snprintf( str, MAX_STR_LEN+1, _("Do you really want to Surrender to %s's Kingdom?"), nation_array[cur_talk_msg.to_nation_recno]->king_name(1) );
492 choice_question = str;
493
494 add_talk_choice( _("Confirm."), 1 );
495
496 return 1;
497 }
498 //----- End of function TalkRes::add_surrender_choices -------//
499
500
501 //------- Begin of function TalkRes::can_send_any_msg --------//
502 //
can_send_any_msg(int toNationRecno,int fromNationRecno)503 int TalkRes::can_send_any_msg(int toNationRecno, int fromNationRecno)
504 {
505 return wait_msg_count(toNationRecno, fromNationRecno) < MAX_WAIT_MSG_PER_NATION;
506 }
507 //-------- End of function TalkRes::can_send_any_msg ---------//
508
509
510 //------- Begin of function TalkRes::can_send_msg --------//
511 //
512 // return whether one specific nation can send a specific message
513 // to another specific nation.
514 //
can_send_msg(int toNationRecno,int fromNationRecno,int talkId)515 int TalkRes::can_send_msg(int toNationRecno, int fromNationRecno, int talkId)
516 {
517 Nation* fromNation = nation_array[fromNationRecno];
518 Nation* toNation = nation_array[toNationRecno];
519
520 NationRelation *nationRelation = fromNation->get_relation(toNationRecno);
521 int relationStatus = nationRelation->status;
522
523 switch( talkId )
524 {
525 case TALK_PROPOSE_TRADE_TREATY:
526 return relationStatus !=NATION_ALLIANCE && // allied nations are oblied to trade with each other
527 relationStatus !=NATION_HOSTILE &&
528 !toNation->get_relation(fromNationRecno)->trade_treaty;
529
530 case TALK_PROPOSE_FRIENDLY_TREATY:
531 return relationStatus==NATION_TENSE ||
532 relationStatus==NATION_NEUTRAL;
533
534 case TALK_PROPOSE_ALLIANCE_TREATY:
535 return relationStatus==NATION_FRIENDLY ||
536 relationStatus==NATION_NEUTRAL;
537
538 case TALK_END_TRADE_TREATY:
539 return relationStatus !=NATION_ALLIANCE && // allied nations are oblied to trade with each other
540 toNation->get_relation(fromNationRecno)->trade_treaty;
541
542 case TALK_END_FRIENDLY_TREATY:
543 return relationStatus==NATION_FRIENDLY;
544
545 case TALK_END_ALLIANCE_TREATY:
546 return relationStatus==NATION_ALLIANCE;
547
548 case TALK_REQUEST_MILITARY_AID:
549 return fromNation->is_at_war() &&
550 (relationStatus==NATION_FRIENDLY ||
551 relationStatus==NATION_ALLIANCE);
552
553 case TALK_REQUEST_TRADE_EMBARGO:
554 return relationStatus==NATION_FRIENDLY ||
555 relationStatus==NATION_ALLIANCE;
556
557 case TALK_REQUEST_CEASE_WAR:
558 return relationStatus==NATION_HOSTILE;
559
560 case TALK_REQUEST_DECLARE_WAR: // can only request an allied nation to declare war with another nation
561 {
562 if( relationStatus != NATION_ALLIANCE )
563 return 0;
564
565 //--- see if this nation has an enemy right now ---//
566
567 for( int i=nation_array.size() ; i>0 ; i-- )
568 {
569 if( nation_array.is_deleted(i) )
570 continue;
571
572 if( fromNation->get_relation(i)->status == NATION_HOSTILE )
573 return 1;
574 }
575
576 return 0;
577 }
578
579 case TALK_REQUEST_BUY_FOOD:
580 return relationStatus != NATION_HOSTILE;
581
582 case TALK_DECLARE_WAR:
583 return relationStatus != NATION_ALLIANCE &&
584 relationStatus != NATION_FRIENDLY &&
585 relationStatus != NATION_HOSTILE;
586
587 case TALK_GIVE_TRIBUTE:
588 case TALK_DEMAND_TRIBUTE:
589 return relationStatus <= NATION_NEUTRAL;
590
591 case TALK_GIVE_AID:
592 case TALK_DEMAND_AID:
593 return relationStatus >= NATION_FRIENDLY;
594
595 case TALK_GIVE_TECH:
596 return fromNation->total_tech_level() > 0;
597
598 case TALK_DEMAND_TECH:
599 return toNation->total_tech_level() > 0;
600
601 case TALK_REQUEST_SURRENDER:
602 return 1;
603
604 case TALK_SURRENDER:
605 return 1;
606 }
607
608 return 0;
609 }
610 //-------- End of function TalkRes::can_send_msg ---------//
611
612
613 //------- Begin of function TalkRes::add_talk_choice --------//
614 //
add_talk_choice(const char * talkStr,int talkPara)615 void TalkRes::add_talk_choice(const char* talkStr, int talkPara)
616 {
617 err_when( talk_choice_count >= MAX_TALK_CHOICE );
618
619 talk_choice_count++;
620
621 talk_choice_array[talk_choice_count-1].str = talkStr;
622 talk_choice_array[talk_choice_count-1].para = talkPara;
623 }
624 //-------- End of function TalkRes::add_talk_choice ---------//
625
626
627 //------- Begin of function TalkRes::ai_send_talk_msg --------//
628 //
629 // <int> toNationRecno - the nation which this message is being sent to
630 // <int> fromNationRecno - the nation which this message is from
631 // <int> talkId - id. of the talk
632 // [int] talkPara1 - talk para 1
633 // [int] talkPara2 - talk para 2
634 // [int] forceSend - if 1, then should_diplomacy_retry() won't
635 // be checked. (default: 0)
636 //
ai_send_talk_msg(int toNationRecno,int fromNationRecno,int talkId,int talkPara1,int talkPara2,int forceSend)637 int TalkRes::ai_send_talk_msg(int toNationRecno, int fromNationRecno, int talkId, int talkPara1, int talkPara2, int forceSend)
638 {
639 Nation* fromNation = nation_array[fromNationRecno];
640
641 if( !fromNation->is_ai() )
642 return 0;
643
644 //--- first check again if the nation should send the message now ---//
645
646 if( !forceSend )
647 {
648 if( !fromNation->should_diplomacy_retry(talkId, toNationRecno) )
649 return 0;
650 }
651
652 //-------- avoid send opposite message too soon ----//
653
654 int oppTalkId=0;
655
656 switch( talkId )
657 {
658 case TALK_PROPOSE_ALLIANCE_TREATY:
659 oppTalkId = TALK_END_FRIENDLY_TREATY;
660 break;
661
662 case TALK_GIVE_TRIBUTE:
663 oppTalkId = TALK_DEMAND_TRIBUTE;
664 break;
665
666 case TALK_DEMAND_TRIBUTE:
667 oppTalkId = TALK_GIVE_TRIBUTE;
668 break;
669
670 case TALK_GIVE_AID:
671 oppTalkId = TALK_DEMAND_AID;
672 break;
673
674 case TALK_DEMAND_AID:
675 oppTalkId = TALK_GIVE_AID;
676 break;
677
678 case TALK_GIVE_TECH:
679 oppTalkId = TALK_DEMAND_TECH;
680 break;
681
682 case TALK_DEMAND_TECH:
683 oppTalkId = TALK_GIVE_TECH;
684 break;
685 }
686
687 if( oppTalkId )
688 {
689 fromNation->get_relation(toNationRecno)->
690 last_talk_reject_date_array[oppTalkId-1] = info.game_date;
691 }
692
693 //------------------------------------------//
694
695 TalkMsg talkMsg;
696
697 memset(&talkMsg, 0, sizeof(TalkMsg));
698
699 talkMsg.to_nation_recno = toNationRecno;
700 talkMsg.from_nation_recno = fromNationRecno;
701 talkMsg.talk_id = talkId;
702 talkMsg.talk_para1 = talkPara1;
703 talkMsg.talk_para2 = talkPara2;
704
705 err_when( !nation_array[fromNationRecno]->is_ai() );
706
707 send_talk_msg( &talkMsg, COMMAND_AI );
708
709 return 1;
710 }
711 //-------- End of function TalkRes::ai_send_talk_msg ---------//
712
713
714 //------- Begin of function TalkRes::send_talk_msg --------//
715 //
716 // Now records in talk_msg_array cannot be deleted as
717 // news_array.diplomacy() use recno to refer to talk_msg_array.
718 //
send_talk_msg(TalkMsg * talkMsgPtr,char remoteAction)719 void TalkRes::send_talk_msg(TalkMsg* talkMsgPtr, char remoteAction)
720 {
721 //-------- send multiplayer -----------//
722
723 if( !remoteAction && remote.is_enable() )
724 {
725 // packet strcture : <TalkMsg>
726 char* dataPtr = remote.new_send_queue_msg(MSG_SEND_TALK_MSG, sizeof(TalkMsg) );
727
728 memcpy( dataPtr, talkMsgPtr, sizeof(TalkMsg) );
729 return;
730 }
731
732 //------ the TalkMsg::reply_type ------//
733
734 if( talkMsgPtr->is_reply_needed() )
735 talkMsgPtr->reply_type = REPLY_WAITING;
736 else
737 talkMsgPtr->reply_type = REPLY_NOT_NEEDED;
738
739 //-- If this is an AI message check if this message has already been sent --//
740
741 if( nation_array[talkMsgPtr->from_nation_recno]->nation_type == NATION_AI )
742 {
743 if( talkMsgPtr->reply_type == REPLY_WAITING ) // for messages that do not need a reply, duplication in the message log is allowed.
744 {
745 if( is_talk_msg_exist(talkMsgPtr, 0) ) // 0-don't check talk_para1 & talk_para2
746 return;
747 }
748 }
749
750 //--- in a multiplayer game, when the msg comes back from the network, can_send_msg might be different, so we have to check it again ---//
751
752 if( !can_send_msg( talkMsgPtr->to_nation_recno, talkMsgPtr->from_nation_recno, talkMsgPtr->talk_id ) )
753 return;
754
755 //-------- send the message now ---------//
756
757 send_talk_msg_now(talkMsgPtr);
758
759 //---- if it's a notification message ----//
760
761 if( talkMsgPtr->reply_type == REPLY_NOT_NEEDED )
762 talkMsgPtr->process_accepted_reply();
763 }
764 //-------- End of function TalkRes::send_talk_msg ---------//
765
766
767 //------- Begin of function TalkRes::send_talk_msg_now --------//
768 //
send_talk_msg_now(TalkMsg * talkMsgPtr)769 void TalkRes::send_talk_msg_now(TalkMsg* talkMsgPtr)
770 {
771 //--------- add the message ------------//
772
773 Nation* toNation = nation_array[talkMsgPtr->to_nation_recno];
774
775 talkMsgPtr->date = info.game_date;
776 talkMsgPtr->relation_status = toNation->get_relation_status(talkMsgPtr->from_nation_recno);
777
778 talk_msg_array.linkin( talkMsgPtr );
779
780 err_when( nation_array.is_deleted(talkMsgPtr->from_nation_recno) );
781 err_when( nation_array.is_deleted(talkMsgPtr->to_nation_recno) );
782
783 //--------------------------------------//
784
785 switch(toNation->nation_type)
786 {
787 case NATION_OWN: // can be from both AI or a remote player
788 news_array.diplomacy( talk_msg_array.recno() );
789 // ###### begin Gilbert 9/10 ########//
790 // sound effect
791 if( toNation->get_relation(talkMsgPtr->from_nation_recno)->has_contact )
792 se_ctrl.immediate_sound(talkMsgPtr->talk_id==TALK_DECLARE_WAR ? (char*)"DECL_WAR":(char*)"GONG");
793 // ###### end Gilbert 9/10 ########//
794 break;
795
796 case NATION_AI:
797 if( talkMsgPtr->reply_type == REPLY_WAITING )
798 {
799 //-- put the message in the receiver's action queue --//
800
801 toNation->add_action( 0, 0, 0, 0, ACTION_AI_PROCESS_TALK_MSG, talk_msg_array.recno() );
802 }
803 else if( talkMsgPtr->reply_type == REPLY_NOT_NEEDED )
804 {
805 //--- notify the receiver immediately ---//
806
807 toNation->notify_talk_msg( talkMsgPtr );
808 }
809 else
810 err_here();
811 break;
812
813 case NATION_REMOTE: // do nothing here as NATION_OWN handle both msg from AI and a remote player
814 break;
815 }
816 }
817 //-------- End of function TalkRes::send_talk_msg_now ---------//
818
819
820 //------- Begin of function TalkRes::reply_talk_msg --------//
821 //
822 // If the nation which receives this message decides to accept
823 // the offer of this message, this function is called.
824 //
825 // <int> talkMsgRecno - the recno of the TalkMsg
826 // <char> replyType - reply type, either REPLY_ACCEPT or REPLY_REJECT
827 // <char> remoteAction - remote action type
828 //
reply_talk_msg(int talkMsgRecno,char replyType,char remoteAction)829 void TalkRes::reply_talk_msg(int talkMsgRecno, char replyType, char remoteAction)
830 {
831 //-------- send multiplayer -----------//
832
833 if( !remoteAction && remote.is_enable() )
834 {
835 // packet structure : <talkRecno:int> <reply type:char> <padding:char>
836 char* charPtr = remote.new_send_queue_msg( MSG_REPLY_TALK_MSG, sizeof(int)+2*sizeof(char) );
837 *(int *)charPtr = talkMsgRecno;
838 charPtr[sizeof(int)] = replyType;
839 charPtr[sizeof(int)+sizeof(char)] = 0;
840 return;
841 }
842
843 //-------------------------------------//
844
845 err_when( is_talk_msg_deleted(talkMsgRecno) );
846
847 TalkMsg* talkMsgPtr = get_talk_msg(talkMsgRecno);
848 Nation* fromNation = nation_array[talkMsgPtr->from_nation_recno];
849
850 err_when( talkMsgPtr->reply_type == REPLY_NOT_NEEDED );
851 err_when( replyType != REPLY_ACCEPT && replyType != REPLY_REJECT );
852
853 talkMsgPtr->reply_type = replyType;
854 talkMsgPtr->reply_date = info.game_date;
855
856 switch( fromNation->nation_type )
857 {
858 case NATION_OWN:
859 news_array.diplomacy( talkMsgRecno );
860 // ###### begin Gilbert 9/10 ########//
861 // sound effect
862 se_ctrl.immediate_sound("GONG");
863 // ###### end Gilbert 9/10 ########//
864 break;
865
866 case NATION_AI:
867 fromNation->ai_notify_reply( talkMsgRecno ); // notify the AI nation about this reply.
868 break;
869
870 case NATION_REMOTE:
871 break;
872 }
873
874 //------- if the offer is accepted -------//
875
876 if( talkMsgPtr->reply_type == REPLY_ACCEPT )
877 talkMsgPtr->process_accepted_reply();
878
879 //--- if the player has replyed the message, remove it from the news display ---//
880
881 if( talkMsgPtr->to_nation_recno == nation_array.player_recno )
882 news_array.remove(NEWS_DIPLOMACY, talkMsgRecno);
883 }
884 //-------- End of function TalkRes::reply_talk_msg ---------//
885
886
887 //------- Begin of function TalkRes::disp_talk --------//
888
disp_talk()889 void TalkRes::disp_talk()
890 {
891 //----- if the player can send no more message to the nation ---//
892
893 if( !info.player_reply_mode &&
894 !can_send_any_msg(cur_talk_msg.to_nation_recno, nation_array.player_recno) )
895 {
896 if( !( choice_question && strcmp(choice_question, MESSAGE_SENT_STR)==0 ) ) // if it's currently displaying the has sent notification, display that message
897 {
898 const char* msgStr = _("You've sent too many messages to this kingdom. You cannot send any new messages until the existing ones are processed.");
899
900 font_san.put_paragraph( TALK_X1, TALK_Y1, TALK_X2, TALK_Y2, msgStr, 4 );
901 return;
902 }
903 }
904
905 //--- return whether the talk choices should be refreshed. ---//
906
907 if( !reply_talk_msg_recno && !cur_talk_msg.talk_id ) // not replying and haven't selected a message type yet.
908 {
909 for( int i=0 ; i<MAX_TALK_TYPE ; i++ )
910 {
911 if( available_talk_id_array[i] != can_send_msg(cur_talk_msg.to_nation_recno, cur_talk_msg.from_nation_recno, i+1) )
912 {
913 set_talk_choices();
914 break;
915 }
916 }
917 }
918
919 //------ repaint the area for displaying the sub-title ------//
920
921 int y=TALK_Y1;
922 String str;
923
924 //----- if there is a question on the choices ------//
925
926 if( choice_question )
927 {
928 font_san.put( TALK_X1+3, y, choice_question );
929 y+=TALK_LINE_HEIGHT;
930 }
931
932 if( choice_question_second_line ) // second line message
933 {
934 font_san.put( TALK_X1+3, y, choice_question_second_line );
935 y+=TALK_LINE_HEIGHT;
936 }
937
938 //--------- display talk choices ---------//
939
940 for( int i=0 ; i<talk_choice_count ; i++, y+=TALK_LINE_HEIGHT )
941 {
942 if( i+1 == cur_choice_id ) // this is the one the mouse cursor is currently on
943 vga_back.adjust_brightness( TALK_X1, y, TALK_X2, y+TALK_LINE_HEIGHT-1, -3 );
944
945 if( choice_question )
946 {
947 str = "- "; // display bullets in front of the text strings
948 str += talk_choice_array[i].str;
949 }
950 else
951 str = talk_choice_array[i].str;
952
953 font_san.put_paragraph( TALK_X1+3, y+2, TALK_X2-3, y+font_san.height()+1, str );
954 }
955 }
956 //-------- End of function TalkRes::disp_talk ---------//
957
958
959 //------- Begin of function TalkRes::detect_talk_choices --------//
960 //
detect_talk_choices()961 int TalkRes::detect_talk_choices()
962 {
963 int i, y=TALK_Y1;
964 int newChoiceId=0;
965
966 if( choice_question )
967 y+=TALK_LINE_HEIGHT;
968
969 if( choice_question_second_line )
970 y+=TALK_LINE_HEIGHT;
971
972 for( i=0 ; i<talk_choice_count ; i++, y+=TALK_LINE_HEIGHT )
973 {
974 if( mouse.in_area(TALK_X1, y, TALK_X2, y+TALK_LINE_HEIGHT-1) )
975 {
976 newChoiceId = i+1;
977 break;
978 }
979 }
980
981 //-------- refresh talk display -------//
982
983 if( newChoiceId != cur_choice_id )
984 {
985 cur_choice_id = newChoiceId;
986 disp_talk();
987 }
988
989 //------ if pressed on one of the talks ------//
990
991 if( newChoiceId && mouse.single_click(TALK_X1, y, TALK_X2, y+TALK_LINE_HEIGHT-1) )
992 return newChoiceId;
993
994 return 0;
995 }
996 //-------- End of function TalkRes::detect_talk_choices ---------//
997
998
999 //------- Begin of function TalkRes::detect_talk --------//
1000 //
detect_talk()1001 int TalkRes::detect_talk()
1002 {
1003 //----- if the message which player is reply is deleted (this can only happen when the player stays on the reply message screen for almost 1 year without doing anything and then the message is automatically deleted after having being kept for 1 year ---//
1004
1005 if( reply_talk_msg_recno &&
1006 is_talk_msg_deleted(reply_talk_msg_recno) ) // the message may become invalid during the replying period
1007 {
1008 sys.set_view_mode(save_view_mode);
1009
1010 if( save_view_mode == MODE_NATION )
1011 {
1012 cur_talk_msg.talk_id = 0;
1013 init_conversion(cur_talk_msg.to_nation_recno);
1014 }
1015
1016 return 0;
1017 }
1018
1019 //------------------------------------------//
1020
1021 int choiceId = detect_talk_choices();
1022
1023 if( !choiceId )
1024 return 0;
1025
1026 int choicePara = talk_choice_array[choiceId-1].para;
1027
1028 //---- if the player is replying message from other nation ----//
1029
1030 if( reply_talk_msg_recno )
1031 {
1032 if( !is_talk_msg_deleted(reply_talk_msg_recno) ) // the message may become invalid during the replying period
1033 {
1034 if( choicePara==1 )
1035 reply_talk_msg(reply_talk_msg_recno, REPLY_ACCEPT, COMMAND_PLAYER);
1036 else
1037 reply_talk_msg(reply_talk_msg_recno, REPLY_REJECT, COMMAND_PLAYER);
1038 }
1039
1040 sys.set_view_mode(save_view_mode);
1041
1042 if( save_view_mode == MODE_NATION )
1043 {
1044 cur_talk_msg.talk_id = 0;
1045 init_conversion(cur_talk_msg.to_nation_recno);
1046 }
1047
1048 return 1;
1049 }
1050
1051 //---------------------------------------//
1052
1053 if( strcmp( talk_choice_array[choiceId-1].str, _("Cancel.") ) == 0 ||
1054 (choice_question && strcmp( choice_question, MESSAGE_SENT_STR )==0) )
1055 {
1056 cur_talk_msg.talk_id = 0;
1057 init_conversion(cur_talk_msg.to_nation_recno);
1058 return 1;
1059 }
1060
1061 //------ set the current choice to cur_talk_msg -------//
1062
1063 if( cur_talk_msg.talk_id == 0 )
1064 cur_talk_msg.talk_id = choicePara;
1065
1066 else if( cur_talk_msg.talk_para1 == 0 )
1067 cur_talk_msg.talk_para1 = choicePara;
1068
1069 else if( cur_talk_msg.talk_para2 == 0 )
1070 cur_talk_msg.talk_para2 = choicePara;
1071
1072 else
1073 err_here();
1074
1075 //------ prepare the next available choices ------//
1076
1077 if( !set_talk_choices() ) // the talk is complete
1078 {
1079 send_talk_msg( &cur_talk_msg, COMMAND_PLAYER );
1080
1081 //--- the message has been sent, display notification message ---//
1082
1083 choice_question = MESSAGE_SENT_STR;
1084 choice_question_second_line = NULL;
1085
1086 talk_choice_count = 0;
1087 add_talk_choice( _("Continue."), 0 );
1088 }
1089
1090 return 1;
1091 }
1092 //-------- End of function TalkRes::detect_talk ---------//
1093
1094
1095 //------- Begin of function TalkRes::get_talk_msg --------//
1096
get_talk_msg(int recNo)1097 TalkMsg* TalkRes::get_talk_msg(int recNo)
1098 {
1099 err_when( recNo < 1 || recNo > talk_msg_array.size() );
1100
1101 TalkMsg* talkMsg = (TalkMsg*) talk_msg_array.get(recNo);
1102
1103 if( !talkMsg->talk_id )
1104 err.run( "get_talk_msg() error, the TalkMsg is deleted." );
1105
1106 return talkMsg;
1107 }
1108 //-------- End of function TalkRes::get_talk_msg ---------//
1109
1110
1111 //------- Begin of function TalkRes::is_talk_msg_deleted --------//
1112
is_talk_msg_deleted(int recNo)1113 int TalkRes::is_talk_msg_deleted(int recNo)
1114 {
1115 if( recNo < 1 || recNo > talk_msg_array.size() )
1116 return 1;
1117
1118 TalkMsg* talkMsg = (TalkMsg*) talk_msg_array.get(recNo);
1119
1120 return talkMsg->talk_id==0;
1121 }
1122 //-------- End of function TalkRes::is_talk_msg_deleted ---------//
1123
1124
1125 //------- Begin of function TalkRes::next_day --------//
1126
next_day()1127 void TalkRes::next_day()
1128 {
1129 if( info.game_date%7 == 0 )
1130 process_talk_msg();
1131 }
1132 //-------- End of function TalkRes::next_day ---------//
1133
1134
1135 //------ Begin of function TalkRes::process_talk_msg ------//
1136 //
process_talk_msg()1137 void TalkRes::process_talk_msg()
1138 {
1139 int i;
1140 TalkMsg* talkMsg;
1141
1142 for( i=talk_msg_count() ; i>0 ; i-- )
1143 {
1144 if( is_talk_msg_deleted(i) )
1145 continue;
1146
1147 talkMsg = get_talk_msg(i);
1148
1149 //--------------------------------------------------------//
1150 // If this is an AI message and there is no response from
1151 // the player after one month the message has been sent,
1152 // it presumes that the message has been rejected.
1153 //--------------------------------------------------------//
1154
1155 if( nation_array[talkMsg->from_nation_recno]->nation_type == NATION_AI &&
1156 talkMsg->reply_type == REPLY_WAITING &&
1157 info.game_date > talkMsg->date + DISP_NEWS_DAYS )
1158 {
1159 talkMsg->reply_type = REPLY_REJECT;
1160
1161 nation_array[talkMsg->from_nation_recno]->ai_notify_reply(i);
1162 }
1163
1164 //--- delete the talk message after a year ---//
1165
1166 if( info.game_date > talkMsg->date + TALK_MSG_KEEP_DAYS )
1167 del_talk_msg(i);
1168 }
1169 }
1170 //-------- End of function TalkRes::process_talk_msg ---------//
1171
1172
1173 //------ Begin of function TalkRes::del_talk_msg ------//
1174 //
del_talk_msg(int talkMsgRecno)1175 void TalkRes::del_talk_msg(int talkMsgRecno)
1176 {
1177 err_when( is_talk_msg_deleted(talkMsgRecno) );
1178
1179 TalkMsg* talkMsg = get_talk_msg(talkMsgRecno);
1180
1181 //--- if this message is sent to an AI nation ---//
1182
1183 Nation* nationPtr = nation_array[talkMsg->to_nation_recno];
1184
1185 if( nationPtr->nation_type == NATION_AI &&
1186 (talkMsg->reply_type == REPLY_NOT_NEEDED || // even if a reply is not needed, the message will still be sent to the AI for notification.
1187 talkMsg->reply_type == REPLY_WAITING) )
1188 {
1189 //--- it may still have the message in its action queue ---//
1190
1191 ActionNode* actionNode;
1192
1193 for( int i=nationPtr->action_count() ; i>0 ; i-- )
1194 {
1195 actionNode = nationPtr->get_action(i);
1196
1197 if( actionNode->action_mode == ACTION_AI_PROCESS_TALK_MSG &&
1198 actionNode->action_para == talkMsgRecno )
1199 {
1200 nationPtr->del_action(i);
1201 break;
1202 }
1203 }
1204 }
1205
1206 //----- delete the message from the news array -----//
1207
1208 if( talkMsg->to_nation_recno == nation_array.player_recno ||
1209 talkMsg->from_nation_recno == nation_array.player_recno )
1210 {
1211 news_array.remove( NEWS_DIPLOMACY, talkMsgRecno );
1212 }
1213
1214 //----- link it out from talk_msg_array -----//
1215
1216 talk_msg_array.linkout(talkMsgRecno);
1217 }
1218 //-------- End of function TalkRes::del_talk_msg ---------//
1219
1220
1221 //------- Begin of function TalkRes::is_talk_msg_exist --------//
1222 //
1223 // <int> checkPara - whether check talk_para1 and talk_para2
1224 // in comparing TalkMsg.
1225 //
1226 // return : <int> >0 - the recno of the talk msg that already exists
1227 // ==0 - not found
1228 //
is_talk_msg_exist(TalkMsg * thisTalkMsg,int checkPara)1229 int TalkRes::is_talk_msg_exist(TalkMsg* thisTalkMsg, int checkPara)
1230 {
1231 int i;
1232 TalkMsg* talkMsg;
1233
1234 for( i=talk_msg_count() ; i>0 ; i-- )
1235 {
1236 if( is_talk_msg_deleted(i) )
1237 continue;
1238
1239 talkMsg = get_talk_msg(i);
1240
1241 if( talkMsg->reply_type == REPLY_WAITING ||
1242 talkMsg->reply_type == REPLY_NOT_NEEDED )
1243 {
1244 if( talkMsg->talk_id == thisTalkMsg->talk_id &&
1245 talkMsg->from_nation_recno == thisTalkMsg->from_nation_recno &&
1246 talkMsg->to_nation_recno == thisTalkMsg->to_nation_recno )
1247 {
1248 if( checkPara )
1249 {
1250 if( talkMsg->talk_para1 == thisTalkMsg->talk_para1 &&
1251 talkMsg->talk_para2 == thisTalkMsg->talk_para2 )
1252 {
1253 return i;
1254 }
1255 }
1256 else
1257 return i;
1258 }
1259 }
1260 }
1261
1262 return 0;
1263 }
1264 //-------- End of function TalkRes::is_talk_msg_exist ---------//
1265
1266
1267 //------- Begin of function TalkRes::wait_msg_count --------//
1268 //
1269 // Return the number of messages sent to the nation waiting
1270 // for its reply.
1271 //
wait_msg_count(int toNationRecno,int fromNationRecno)1272 int TalkRes::wait_msg_count(int toNationRecno, int fromNationRecno)
1273 {
1274 int i, waitMsgCount=0;
1275 TalkMsg* talkMsg;
1276
1277 for( i=talk_msg_count() ; i>0 ; i-- )
1278 {
1279 if( is_talk_msg_deleted(i) )
1280 continue;
1281
1282 talkMsg = get_talk_msg(i);
1283
1284 if( talkMsg->reply_type == REPLY_WAITING &&
1285 talkMsg->to_nation_recno == toNationRecno &&
1286 talkMsg->from_nation_recno == fromNationRecno &&
1287 info.game_date < talkMsg->date + 30 ) // only count message in a month
1288 {
1289 waitMsgCount++;
1290 }
1291 }
1292
1293 return waitMsgCount;
1294 }
1295 //-------- End of function TalkRes::wait_msg_count ---------//
1296
1297
1298 //------- Begin of function TalkRes::player_reply --------//
1299
player_reply(int talkMsgRecno)1300 void TalkRes::player_reply(int talkMsgRecno)
1301 {
1302 //------- set the reply choices --------//
1303
1304 err_when( is_talk_msg_deleted(talkMsgRecno) );
1305
1306 TalkMsg* talkMsg = get_talk_msg(talkMsgRecno);
1307
1308 if( nation_array.is_deleted(talkMsg->from_nation_recno) )
1309 return;
1310
1311 init_conversion(talkMsg->from_nation_recno);
1312
1313 talk_choice_count = 0;
1314 cur_choice_id = 0;
1315 reply_talk_msg_recno = talkMsgRecno;
1316
1317 //--------- add talk choices ---------//
1318
1319 static String msgStr, msgStr2;
1320
1321 msgStr = talkMsg->msg_str(nation_array.player_recno); // make a static copy of it.
1322 choice_question = msgStr;
1323
1324 //---- see if this message has a second line -----//
1325
1326 msgStr2 = talkMsg->msg_str(nation_array.player_recno, 0, 1); // 1-display the second line of the question
1327
1328 if( msgStr!=msgStr2 )
1329 choice_question_second_line = msgStr2;
1330 else
1331 choice_question_second_line = NULL;
1332
1333 //--------- add choices to the question ---------//
1334
1335 if( talkMsg->can_accept() ) // whether the replier can accept the request or demand of the message
1336 add_talk_choice( _("Accept."), 1 );
1337
1338 add_talk_choice( _("Reject."), 0 );
1339
1340 //--- switch to the nation report mode and go to the diplomacy mode ---//
1341
1342 info.init_player_reply( talkMsg->from_nation_recno );
1343
1344 save_view_mode = sys.view_mode;
1345
1346 sys.set_view_mode(MODE_NATION);
1347 }
1348 //-------- End of function TalkRes::player_reply ---------//
1349
1350
1351 //------- Begin of function TalkRes::del_all_nation_msg --------//
1352 //
1353 // Delete all messages related to this nation.
1354 //
del_all_nation_msg(int nationRecno)1355 void TalkRes::del_all_nation_msg(int nationRecno)
1356 {
1357 int i;
1358 TalkMsg* talkMsg;
1359
1360 for( i=talk_msg_count() ; i>0 ; i-- )
1361 {
1362 if( is_talk_msg_deleted(i) )
1363 continue;
1364
1365 talkMsg = get_talk_msg(i);
1366
1367 // If the nation is referenced anywhere in the talk-message, is_valid_to_disp() will return 0 and the talk message should than be deleted. Need to explicitly specify nation as it hasn't been deleted yet.
1368 if( !talkMsg->is_valid_to_disp(nationRecno) )
1369 {
1370 del_talk_msg(i);
1371 }
1372 }
1373 }
1374 //-------- End of function TalkRes::del_all_nation_msg ---------//
1375
1376
1377 #define ASCII_ZERO 0x30
1378 //------ Begin of static function select_nation_color ------//
1379 //
select_nation_color(char nation_color)1380 static char* select_nation_color(char nation_color)
1381 {
1382 static char colorCodeStr[] = "@COL0 ";
1383
1384 colorCodeStr[4] = ASCII_ZERO + nation_color;
1385
1386 return colorCodeStr;
1387 }
1388 //------ End of static function select_nation_color ------//
1389