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 : OUNIT.CPP
22 //Description : Object Unit
23
24 #include <ALL.h>
25 #include <ODATE.h>
26 #include <OWORLD.h>
27 #include <OVGA.h>
28 #include <OSTR.h>
29 #include <ONEWS.h>
30 #include <OREBEL.h>
31 #include <OSPY.h>
32 #include <ONATION.h>
33 #include <OFONT.h>
34 #include <OBULLET.h>
35 #include <OGAME.h>
36 #include <OTOWN.h>
37 #include <ORACERES.h>
38 #include <ORAWRES.h>
39 #include <OPOWER.h>
40 #include <OU_VEHI.h>
41 #include <OU_MARI.h>
42 #include <OU_MONS.h>
43 #include <OF_CAMP.h>
44 #include <OF_MONS.h>
45 #include <OF_HARB.h>
46 #include <OMONSRES.h>
47 #include <OREMOTE.h>
48 #include <OSYS.h>
49 #include "gettext.h"
50 #include <ConfigAdv.h>
51
52 #if(GAME_FRAMES_PER_DAY!=FRAMES_PER_DAY)
53 #error
54 #endif
55
56 #ifdef NO_DEBUG_UNIT
57 #undef err_when
58 #undef err_here
59 #undef err_if
60 #undef err_else
61 #undef err_now
62 #define err_when(cond)
63 #define err_here()
64 #define err_if(cond)
65 #define err_else
66 #define err_now(msg)
67 #undef DEBUG
68 #endif
69
70 //--------- Begin of function Unit::Unit ---------//
71 //
Unit()72 Unit::Unit()
73 {
74 // ##### patch begin Gilbert 21/1 ######//
75 // unit_id = 0;
76 memset( sizeof(Sprite) + (char *)this, 0, sizeof(Unit) - sizeof(Sprite));
77 // ##### patch end Gilbert 21/1 ######//
78 }
79 //----------- End of function Unit::Unit -----------//
80
81
82 //--------- Begin of function Unit::~Unit ---------//
83 //
~Unit()84 Unit::~Unit()
85 {
86 deinit();
87 }
88 //----------- End of function Unit::~Unit -----------//
89
90
91 //--------- Begin of function Unit::init ---------//
92 //
93 // <int> unitId - the id. of the unit
94 // <int> nationRecno - the recno of nation
95 // [int] rankId - rank id. of the unit (none for non-human unit)
96 // [int] unitLoyalty - loyalty of the unit (none for non-human unit)
97 // [int] startXLoc, startYLoc - the starting location of the unit
98 // (if startXLoc < 0, this is a unit for hire, and is not a unit of the game yet. init_sprite() won't be called for this unit)
99 // (default: -1, -1)
100 //
101 // Note: sprite_recno must be initialized first before calling Unit::init()
102 //
init(int unitId,int nationRecno,int rankId,int unitLoyalty,int startXLoc,int startYLoc)103 void Unit::init(int unitId, int nationRecno, int rankId, int unitLoyalty, int startXLoc, int startYLoc)
104 {
105 //------------ set basic vars -------------//
106
107 nation_recno = (char) nationRecno;
108 rank_id = rankId; // rank_id must be initialized before init_unit_id() as init_unit_id() may overwrite it
109 nation_contribution = 0; // nation_contribution must be initialized before init_unit_id() as init_unit_id() may overwrite it
110
111 if( rank_id == RANK_GENERAL || rank_id == RANK_KING )
112 {
113 team_info = (TeamInfo*) mem_add( sizeof(TeamInfo) );
114 team_info->member_count = 0;
115 team_info->ai_last_request_defense_date = 0;
116 }
117 else
118 team_info = NULL;
119
120 init_unit_id(unitId);
121
122 group_select_id = 0;
123 unit_group_id = unit_array.cur_group_id++;
124 race_id = (char) unit_res[unit_id]->race_id;
125
126 //------- init unit name ---------//
127
128 if( race_id )
129 {
130 name_id = race_res[race_id]->get_new_name_id();
131 }
132 else //---- init non-human unit series no. ----//
133 {
134 if( nation_recno )
135 name_id = ++nation_array[nation_recno]->last_unit_name_id_array[unit_id-1];
136 else
137 name_id = 0;
138 }
139
140 //------- init ai_unit ----------//
141
142 if( nation_recno )
143 ai_unit = nation_array[nation_recno]->nation_type == NATION_AI;
144 else
145 ai_unit = 0;
146
147 err_when( unitLoyalty < 0 || unitLoyalty > 100 );
148
149 //----------------------------------------------//
150
151 ai_action_id = 0;
152 action_misc = ACTION_MISC_STOP;
153 action_misc_para = 0;
154
155 action_mode2 = action_mode = ACTION_STOP;
156 action_para2 = action_para = 0;
157 action_x_loc2 = action_y_loc2 = action_x_loc = action_y_loc = -1;
158 memset(blocked_edge, 0, sizeof(char)*4);
159
160 attack_range = 0; //store the attack range of the current attack mode if the unit is ordered to attack
161
162 leader_unit_recno= 0;
163 team_id = 0;
164 selected_flag = 0;
165
166 waiting_term = 0;
167 swapping = 0; // indicate whether swapping is processed.
168
169 spy_recno = 0;
170
171 range_attack_x_loc = -1;
172 range_attack_y_loc = -1;
173
174 //------- initialize path seek vars -------//
175
176 result_node_array = NULL;
177 result_node_count = result_node_recno = result_path_dist = 0;
178
179 //------- initialize way point vars -------//
180 way_point_array = NULL;
181 way_point_array_size = 0;
182 way_point_count = 0;
183
184 //---------- initialize game vars ----------//
185
186 unit_mode = 0;
187 unit_mode_para = 0;
188
189 max_hit_points = unit_res[unit_id]->hit_points;
190 hit_points = max_hit_points;
191
192 loyalty = unitLoyalty;
193
194 can_guard_flag = 0;
195 can_attack_flag = 1;
196 force_move_flag = 0;
197 ai_no_suitable_action = 0;
198 cur_power = 0;
199 max_power = 0;
200
201 total_reward = 0;
202
203 home_camp_firm_recno = 0;
204
205 seek_path_fail_count = 0;
206 ignore_power_nation = 0;
207 aggressive_mode = 1; // the default mode is 1
208
209 err_when( loyalty<0 || loyalty>100 );
210
211 //--------- init skill potential ---------//
212
213 if( misc.random(10)==0 ) // 1 out of 10 has a higher than normal potential in this skill
214 {
215 skill.skill_potential = 50+misc.random(51); // 50 to 100 potential
216 }
217
218 //------ initialize the base Sprite class -------//
219
220 if( startXLoc >= 0 )
221 init_sprite( startXLoc, startYLoc );
222 else
223 {
224 cur_x = -1;
225 }
226
227 //------------- set attack_dir ------------//
228
229 attack_dir = final_dir;
230
231 //-------------- update loyalty -------------//
232
233 update_loyalty();
234
235 //--------------- init AI info -------------//
236
237 if( ai_unit )
238 {
239 Nation* nationPtr = nation_array[nation_recno];
240
241 if( rank_id==RANK_GENERAL || rank_id==RANK_KING )
242 nationPtr->add_general_info(sprite_recno);
243
244 switch( unit_res[unit_id]->unit_class )
245 {
246 case UNIT_CLASS_CARAVAN:
247 nationPtr->add_caravan_info(sprite_recno);
248 break;
249
250 case UNIT_CLASS_SHIP:
251 nationPtr->add_ship_info(sprite_recno);
252 break;
253 }
254 }
255
256 //----------- init derived class ----------//
257
258 init_derived();
259 }
260 //----------- End of function Unit::init -----------//
261
262
263 //--------- Begin of function Unit::init_unit_id ---------//
264
init_unit_id(int unitId)265 void Unit::init_unit_id(int unitId)
266 {
267 unit_id = unitId;
268
269 UnitInfo* unitInfo = unit_res[unit_id];
270
271 sprite_id = unitInfo->sprite_id;
272 sprite_info = sprite_res[sprite_id];
273
274 mobile_type = unitInfo->mobile_type;
275
276 //--- if this unit is a weapon unit with multiple versions ---//
277
278 set_combat_level(100); // set combat level default to 100, for human units, it will be adjusted later by individual functions
279
280 int techLevel;
281 if( nation_recno &&
282 unitInfo->unit_class == UNIT_CLASS_WEAPON &&
283 (techLevel=unitInfo->nation_tech_level_array[nation_recno-1]) > 0 )
284 {
285 set_weapon_version(techLevel);
286 }
287
288 fix_attack_info();
289
290 //-------- set unit count ----------//
291
292 if( nation_recno )
293 {
294 if( rank_id != RANK_KING )
295 unitInfo->inc_nation_unit_count(nation_recno);
296
297 if( rank_id == RANK_GENERAL )
298 unitInfo->inc_nation_general_count(nation_recno);
299 }
300
301 //--------- increase monster count ----------//
302
303 if( unit_res[unit_id]->unit_class == UNIT_CLASS_MONSTER )
304 unit_res.mobile_monster_count++;
305 }
306 //----------- End of function Unit::init_unit_id -----------//
307
308
309 //--------- Begin of function Unit::deinit_unit_id ---------//
310
deinit_unit_id()311 void Unit::deinit_unit_id()
312 {
313 if( sys.signal_exit_flag )
314 return;
315
316 //-----------------------------------------//
317
318 UnitInfo *unitInfo = unit_res[unit_id];
319
320 if( nation_recno )
321 {
322 if( rank_id != RANK_KING )
323 unitInfo->dec_nation_unit_count(nation_recno);
324
325 if( rank_id == RANK_GENERAL )
326 unitInfo->dec_nation_general_count(nation_recno);
327 }
328
329 //--------- if the unit is a spy ----------//
330 //
331 // A spy has double identity and is counted
332 // by both the true controlling nation and
333 // the deceiving nation.
334 //
335 //-----------------------------------------//
336
337 if( spy_recno && true_nation_recno() != nation_recno )
338 {
339 err_when( !race_id );
340
341 int trueNationRecno = true_nation_recno();
342
343 if( rank_id != RANK_KING )
344 unitInfo->dec_nation_unit_count(trueNationRecno);
345
346 if( rank_id == RANK_GENERAL )
347 unitInfo->dec_nation_general_count(trueNationRecno);
348 }
349
350 //--------- decrease monster count ----------//
351
352 if( unit_res[unit_id]->unit_class == UNIT_CLASS_MONSTER )
353 {
354 unit_res.mobile_monster_count--;
355
356 err_when( unit_res.mobile_monster_count < 0 );
357 }
358 }
359 //----------- End of function Unit::deinit_unit_id -----------//
360
361
362 //--------- Begin of function Unit::set_spy ---------//
363
set_spy(int spyRecno)364 void Unit::set_spy(int spyRecno)
365 {
366 spy_recno = spyRecno;
367 }
368 //---------- End of function Unit::set_spy ---------//
369
370
371 //--------- Begin of function Unit::init_sprite ---------//
372 //
373 // <int> startXLoc, startYLoc - the starting location of the unit
374 //
init_sprite(int startXLoc,int startYLoc)375 void Unit::init_sprite(int startXLoc, int startYLoc)
376 {
377 err_when( !world.get_loc(startXLoc, startYLoc)->can_move(unit_res[unit_id]->mobile_type) );
378 err_when(unit_res[unit_id]->unit_class==UNIT_CLASS_SHIP && (startXLoc%2 || startYLoc%2));
379
380 Sprite::init(unit_res[unit_id]->sprite_id, startXLoc, startYLoc);
381
382 //--------------------------------------------------------------------//
383 // move_to_?_loc is always the current location of the unit as
384 // cur_action == SPRITE_IDLE
385 //--------------------------------------------------------------------//
386 original_action_mode = 0;
387 ai_original_target_x_loc = -1;
388
389 attack_range = 0;
390
391 move_to_x_loc = next_x_loc();
392 move_to_y_loc = next_y_loc();
393
394 go_x = next_x;
395 go_y = next_y;
396
397 //-------- set the cargo_recno -------------//
398
399 char w, h;
400 short x, y;
401
402 err_if(!sprite_recno) // sprite_recno must be initialized first before calling Unit::init()
403 err_here();
404
405 for(h=0, y=startYLoc; h<sprite_info->loc_height; h++, y++)
406 {
407 for(w=0, x=startXLoc; w<sprite_info->loc_width; w++, x++)
408 {
409 err_if( world.get_unit_recno(x, y, mobile_type) ) // it must be 0 to put the sprite in this location
410 err_here();
411 world.set_unit_recno(x, y, mobile_type, sprite_recno);
412 }
413 }
414
415 if( is_own() ||
416 (nation_recno && nation_array[nation_recno]->is_allied_with_player) )
417 {
418 world.unveil(startXLoc, startYLoc, startXLoc+sprite_info->loc_width-1,
419 startYLoc+sprite_info->loc_height-1 );
420
421 world.visit(startXLoc, startYLoc, startXLoc+sprite_info->loc_width-1,
422 startYLoc+sprite_info->loc_height-1, unit_res[unit_id]->visual_range,
423 unit_res[unit_id]->visual_extend);
424 }
425
426 err_when(result_node_array || result_path_dist);
427 }
428 //----------- End of function Unit::init_sprite -----------//
429
430
431 //--------- Begin of function Unit::deinit ---------//
432
deinit()433 void Unit::deinit()
434 {
435 err_when( unit_array.is_truly_deleted(sprite_recno) );
436
437 if( !unit_id )
438 return;
439
440 //-------- if this is a king --------//
441
442 if( !sys.signal_exit_flag && nation_recno )
443 {
444 if( rank_id == RANK_KING ) // check nation_recno because monster kings will die also.
445 {
446 king_die();
447 err_when( unit_array.is_truly_deleted(sprite_recno) );
448 }
449 else if( rank_id == RANK_GENERAL )
450 {
451 general_die();
452 err_when( unit_array.is_truly_deleted(sprite_recno) );
453 }
454 }
455
456 //------------ free up team_info -----------//
457
458 if( team_info )
459 {
460 mem_del(team_info);
461 team_info = NULL;
462 }
463
464 //---- if this is a general, deinit its link with its soldiers ----//
465 //
466 // We do not use team_info because monsters and rebels also use
467 // leader_unit_recno and they do not use keep the member info
468 // in team_info.
469 //
470 //-----------------------------------------------------------------//
471
472 if( rank_id == RANK_GENERAL || rank_id == RANK_KING )
473 {
474 for( int i=unit_array.size() ; i>0 ; i-- )
475 {
476 if( unit_array.is_deleted(i) )
477 continue;
478
479 if( unit_array[i]->leader_unit_recno == sprite_recno )
480 unit_array[i]->leader_unit_recno = 0;
481 }
482 }
483
484 //----- if this is a unit on a ship ------//
485
486 if( unit_mode == UNIT_MODE_ON_SHIP )
487 {
488 if( !unit_array.is_deleted(unit_mode_para) ) // the ship may have been destroyed at the same time. Actually when the ship is destroyed, all units onboard are killed and this function is called.
489 {
490 Unit* unitPtr = unit_array[unit_mode_para];
491
492 err_when( unit_res[unitPtr->unit_id]->unit_class != UNIT_CLASS_SHIP );
493
494 ((UnitMarine*)unitPtr)->del_unit(sprite_recno);
495 }
496 }
497
498 //----- if this is a ship in the harbor -----//
499
500 else if( unit_mode == UNIT_MODE_IN_HARBOR )
501 {
502 if( !firm_array.is_deleted(unit_mode_para) ) // the ship may have been destroyed at the same time. Actually when the ship is destroyed, all firms onboard are killed and this function is called.
503 {
504 Firm* firmPtr = firm_array[unit_mode_para];
505
506 err_when( firmPtr->firm_id != FIRM_HARBOR );
507
508 ((FirmHarbor*)firmPtr)->del_hosted_ship(sprite_recno);
509 }
510 }
511
512 //----- if this unit is a constructor in a firm -------//
513
514 else if( unit_mode == UNIT_MODE_CONSTRUCT )
515 {
516 err_when( firm_array[unit_mode_para]->builder_recno != sprite_recno );
517
518 firm_array[unit_mode_para]->builder_recno = 0;
519 }
520
521 //-------- if this is a spy ---------//
522
523 if( spy_recno )
524 {
525 spy_array.del_spy( spy_recno );
526 spy_recno = 0;
527 }
528
529 //---------- reset command ----------//
530
531 if( power.command_unit_recno == sprite_recno )
532 power.reset_command();
533
534 //-----------------------------------//
535
536 deinit_unit_id();
537
538 //-------- reset seek path ----------//
539
540 reset_path();
541
542 //----- if cur_x == -1, the unit has not yet been hired -----//
543
544 if( cur_x >= 0 )
545 deinit_sprite();
546
547 //------------------------------------------------//
548 //
549 // Prime rule:
550 //
551 // world.get_loc(next_x_loc() and next_y_loc())->cargo_recno
552 // is always = sprite_recno
553 //
554 // no matter what cur_action is.
555 //
556 //------------------------------------------------//
557 //
558 // Relationship between (next_x, next_y) and (cur_x, cur_y)
559 //
560 // when SPRITE_WAIT, SPRITE_IDLE, SPRITE_READY_TO_MOVE,
561 // SPRITE_ATTACK, SPRITE_DIE:
562 //
563 // (next_x, next_y) == (cur_x, cur_y), it's the location of the sprite.
564 //
565 // when SPRITE_MOVE:
566 //
567 // (next_x, next_y) != (cur_x, cur_y)
568 // (next_x, next_y) is where the sprite is moving towards.
569 // (cur_x , cur_y ) is the location of the sprite.
570 //
571 //------------------------------------------------//
572
573 //--------------- deinit AI info -------------//
574
575 if( ai_unit )
576 {
577 if( !nation_array.is_deleted(nation_recno) )
578 {
579 Nation* nationPtr = nation_array[nation_recno];
580
581 if( rank_id==RANK_GENERAL || rank_id==RANK_KING )
582 nationPtr->del_general_info(sprite_recno);
583
584 switch( unit_res[unit_id]->unit_class )
585 {
586 case UNIT_CLASS_CARAVAN:
587 nationPtr->del_caravan_info(sprite_recno);
588 break;
589
590 case UNIT_CLASS_SHIP:
591 nationPtr->del_ship_info(sprite_recno);
592 break;
593 }
594 }
595 }
596
597 //-------------- reset unit_id ---------------//
598
599 unit_id = 0;
600 }
601 //----------- End of function Unit::deinit -----------//
602
603
604 //--------- Begin of function Unit::deinit_sprite ---------//
605 //
606 // [int] keepSelected - keep it selected if it is current selected.
607 // (default: 0)
608 //
deinit_sprite(int keepSelected)609 void Unit::deinit_sprite(int keepSelected)
610 {
611 err_when(result_node_array!=NULL);
612
613 if( cur_x == -1 )
614 return;
615
616 //---- if this unit is led by a leader, only mobile units has leader_unit_recno assigned to a leader -----//
617 // units are still considered mobile when boarding a ship
618
619 if( leader_unit_recno && unit_mode != UNIT_MODE_ON_SHIP )
620 {
621 if( !unit_array.is_deleted(leader_unit_recno) ) // the leader unit may have been killed at the same time
622 unit_array[leader_unit_recno]->del_team_member(sprite_recno);
623
624 leader_unit_recno = 0;
625 }
626
627 //-------- clear the cargo_recno ----------//
628
629 short w, h;
630 short x, y;
631
632 for(h=0, y=next_y_loc(); h<sprite_info->loc_height; h++, y++)
633 {
634 for(w=0, x=next_x_loc(); w<sprite_info->loc_width; w++, x++)
635 {
636 err_if( world.get_unit_recno(x, y, mobile_type) != sprite_recno ) // it must be 0 to put the sprite in this location
637 err_here();
638 world.set_unit_recno(x, y, mobile_type, 0);
639 }
640 }
641
642 cur_x = -1;
643
644 //---- reset other parameters related to this unit ----//
645
646 if( !keepSelected )
647 {
648 if( unit_array.selected_recno == sprite_recno )
649 {
650 unit_array.selected_recno = 0;
651 info.disp();
652 }
653
654 if( power.command_unit_recno == sprite_recno )
655 power.command_id = 0;
656 }
657
658 //------- deinit unit mode -------//
659
660 deinit_unit_mode();
661 }
662 //----------- End of function Unit::deinit_sprite -----------//
663
664
665 //--------- Begin of function Unit::deinit_unit_mode ---------//
666 //
deinit_unit_mode()667 void Unit::deinit_unit_mode()
668 {
669 if( sys.signal_exit_flag )
670 return;
671
672 //----- this unit was defending the town before it gets killed ----//
673
674 if(unit_mode==UNIT_MODE_DEFEND_TOWN)
675 {
676 if(!town_array.is_deleted(unit_mode_para))
677 {
678 Town *townPtr = town_array[unit_mode_para];
679
680 if(nation_recno == townPtr->nation_recno)
681 townPtr->reduce_defender_count();
682 }
683 set_mode(0); // reset mode
684 }
685
686 //----- this is a monster unit defending its town ------//
687
688 else if( unit_mode==UNIT_MODE_MONSTER && unit_mode_para )
689 {
690 if(((UnitMonster*)this)->monster_action_mode!=MONSTER_ACTION_DEFENSE)
691 return;
692
693 FirmMonster* firmMonster = (FirmMonster*) firm_array[unit_mode_para];
694
695 err_when( firmMonster->firm_id != FIRM_MONSTER );
696
697 firmMonster->reduce_defender_count(rank_id);
698 }
699 }
700 //----------- End of function Unit::deinit_unit_mode -----------//
701
702
703 //--------- Begin of function Unit::king_die ---------//
704 //
king_die()705 void Unit::king_die()
706 {
707 //--------- add news ---------//
708
709 news_array.king_die(nation_recno);
710
711 //--- see if the units, firms and towns of the nation are all destroyed ---//
712
713 Nation* nationPtr = nation_array[nation_recno];
714
715 nationPtr->king_unit_recno = 0;
716 }
717 //----------- End of function Unit::king_die -----------//
718
719
720 //--------- Begin of function Unit::general_die ---------//
721 //
general_die()722 void Unit::general_die()
723 {
724 //--------- add news ---------//
725
726 if( nation_recno == nation_array.player_recno )
727 news_array.general_die(sprite_recno);
728 }
729 //----------- End of function Unit::general_die -----------//
730
731
732 //--------- Begin of function Unit::unit_name ---------//
733 //
734 // [int] withTitle - whether return a string with the title of the unit
735 // or not. (default: 1)
736 //
unit_name(int withTitle)737 char* Unit::unit_name(int withTitle)
738 {
739 static String str;
740
741 UnitInfo* unitInfo = unit_res[unit_id];
742
743 //------------------------------------//
744
745 if( race_id )
746 {
747 str = "";
748
749 if( withTitle )
750 {
751 if( unit_mode == UNIT_MODE_REBEL )
752 {
753 if( rank_id == RANK_GENERAL )
754 {
755 str = _("Rebel Leader");
756 str += " ";
757 }
758 }
759 else
760 {
761 if( rank_id == RANK_KING )
762 {
763 str = _("King");
764 str += " ";
765 }
766 else if( rank_id == RANK_GENERAL )
767 {
768 str = _("General");
769 str += " ";
770 }
771 }
772 }
773
774 if( rank_id == RANK_KING ) // use the player name
775 str += nation_array[nation_recno]->king_name();
776 else
777 str += race_res[race_id]->get_name(name_id);
778 }
779 else
780 {
781 str = _(unitInfo->name);
782
783 //--- for weapons, the rank_id is used to store the version of the weapon ---//
784
785 if( unitInfo->unit_class == UNIT_CLASS_WEAPON && get_weapon_version() > 1 )
786 {
787 str += " ";
788 str += misc.roman_number(get_weapon_version());
789 }
790
791 if( unitInfo->unit_class != UNIT_CLASS_GOD ) // God doesn't have any series no.
792 {
793 str += " ";
794 str += name_id; // name id is the series no. of the unit
795 }
796 }
797
798 return str;
799 }
800 //----------- End of function Unit::unit_name ---------//
801
802
803 //--------- Begin of function Unit::set_name ---------//
804 //
805 // Set the name id. of this unit.
806 //
set_name(uint16_t newNameId)807 void Unit::set_name(uint16_t newNameId)
808 {
809 //------- free up the existing name id. ------//
810
811 race_res[race_id]->free_name_id(name_id);
812
813 //------- set the new name id. ---------//
814
815 name_id = newNameId;
816
817 //-------- register usage of the new name id. ------//
818
819 race_res[race_id]->use_name_id(name_id);
820 }
821 //----------- End of function Unit::set_name ---------//
822
823
824 //--------- Begin of function Unit::is_own ---------//
825 //
is_own()826 int Unit::is_own()
827 {
828 return is_nation(nation_array.player_recno);
829 }
830 //----------- End of function Unit::is_own ---------//
831
832
833 //--------- Begin of function Unit::is_own_spy ---------//
834 //
is_own_spy()835 int Unit::is_own_spy()
836 {
837 return spy_recno && spy_array[spy_recno]->true_nation_recno == nation_array.player_recno;
838 }
839 //----------- End of function Unit::is_own_spy ---------//
840
841
842 //--------- Begin of function Unit::is_nation ---------//
843 //
844 // Whether the unit belongs to the specific nation.
845 //
is_nation(int nationRecno)846 int Unit::is_nation(int nationRecno)
847 {
848 if( nation_recno == nationRecno )
849 return 1;
850
851 if( spy_recno && spy_array[spy_recno]->true_nation_recno == nationRecno )
852 return 1;
853
854 return 0;
855 }
856 //----------- End of function Unit::is_nation ---------//
857
858
859 //--------- Begin of function Unit::is_civilian ---------//
860 //
is_civilian()861 int Unit::is_civilian()
862 {
863 return race_id>0 && skill.skill_id != SKILL_LEADING &&
864 unit_mode != UNIT_MODE_REBEL;
865 }
866 //----------- End of function Unit::is_civilian ---------//
867
868
869 //--------- Begin of function Unit::true_nation_recno ---------//
870 //
871 // The true nation recno of the unit, taking care of the
872 // situation where the unit is a spy.
873 //
true_nation_recno()874 int Unit::true_nation_recno()
875 {
876 if( spy_recno )
877 return spy_array[spy_recno]->true_nation_recno;
878 else
879 return nation_recno;
880 }
881 //----------- End of function Unit::true_nation_recno ---------//
882
883
884 //--------- Begin of function Unit::next_day ---------//
885 //
next_day()886 void Unit::next_day()
887 {
888 int unitRecno = sprite_recno;
889
890 err_when( unit_array.is_deleted(unitRecno) );
891
892 err_when( race_id && !is_visible() && unit_mode==0 );
893
894 #ifdef DEBUG
895
896 if( unit_mode == UNIT_MODE_UNDER_TRAINING )
897 {
898 Town* townPtr =town_array[unit_mode_para];
899
900 err_when( townPtr->train_unit_recno != sprite_recno );
901 err_when( townPtr->nation_recno != nation_recno );
902 }
903
904 #endif
905
906 //------- functions for non-independent nations only ------//
907
908 if( nation_recno )
909 {
910 pay_expense();
911
912 if( unit_array.is_deleted(unitRecno) ) // if its hit points go down to 0, is_deleted() will return 1.
913 return;
914
915 //------- update loyalty -------------//
916
917 if( info.game_date%30 == sprite_recno%30 )
918 {
919 update_loyalty();
920 err_when( unit_array.is_deleted(unitRecno) );
921 }
922
923 //------- think about rebeling -------------//
924
925 if( info.game_date%15 == sprite_recno%15 )
926 {
927 if( think_betray() )
928 return;
929 }
930 }
931
932 //------- recover from damage -------//
933
934 if( info.game_date%15 == sprite_recno%15 ) // recover one point per two weeks
935 {
936 process_recover();
937 err_when( unit_array.is_deleted(unitRecno) );
938 }
939
940 //------- restore cur_power --------//
941
942 cur_power += 5;
943
944 if( cur_power > max_power)
945 cur_power = max_power;
946
947 //------- king undie flag (for testing games only) --------//
948
949 if( config.king_undie_flag && rank_id == RANK_KING &&
950 nation_recno && !nation_array[nation_recno]->is_ai() )
951 {
952 hit_points = max_hit_points;
953 }
954
955 //-------- if aggresive_mode is 1 --------//
956
957 if( nation_recno && is_visible() )
958 think_aggressive_action();
959
960 //---------- debug ------------//
961
962 #ifdef DEBUG
963 err_when( unit_res[unit_id]->unit_class != UNIT_CLASS_HUMAN && race_id );
964
965 if( spy_recno )
966 {
967 err_when( spy_array.is_deleted(spy_recno) );
968
969 Spy* spyPtr = spy_array[spy_recno];
970
971 err_when( nation_recno != spyPtr->cloaked_nation_recno );
972
973 if( unit_mode == UNIT_MODE_OVERSEE )
974 {
975 err_when( spyPtr->spy_place != SPY_FIRM );
976 err_when( spyPtr->spy_place_para != unit_mode_para );
977 }
978 else
979 {
980 err_when( spyPtr->spy_place != SPY_MOBILE );
981 err_when( spyPtr->spy_place_para != sprite_recno );
982 }
983 }
984
985 if( leader_unit_recno )
986 {
987 Unit* unitPtr = unit_array[leader_unit_recno];
988
989 err_when( unitPtr->rank_id != RANK_GENERAL && unitPtr->rank_id != RANK_KING );
990 }
991
992 err_when( (rank_id == RANK_GENERAL || rank_id == RANK_KING) &&
993 !team_info );
994
995 if( leader_unit_recno )
996 {
997 err_when( unit_array.is_truly_deleted(leader_unit_recno) );
998 err_when( unit_array[leader_unit_recno]->nation_recno != nation_recno );
999 // err_when( unit_array[leader_unit_recno]->team_id != team_id );
1000 }
1001
1002 err_when( hit_points > max_hit_points );
1003 err_when( max_hit_points == 0 );
1004
1005 err_when( skill.combat_level<=0 );
1006 err_when( skill.combat_level>100 );
1007
1008 err_when( unit_mode==UNIT_MODE_REBEL && spy_recno ); // no rebel spies
1009 err_when( unit_mode==UNIT_MODE_REBEL && nation_recno ); // all rebels must be independent units
1010
1011 err_when( unit_mode==UNIT_MODE_DEFEND_TOWN && spy_recno ); // no rebel spies
1012
1013 err_when( loyalty < 0 || loyalty > 100 );
1014
1015 err_when( skill.skill_id == SKILL_SPYING ); // skill.skill_id should never be SKILL_SPYING, it will be shown in spy_recno if it's a spy
1016
1017 err_when( nation_contribution < 0 );
1018 err_when( nation_contribution > MAX_NATION_CONTRIBUTION );
1019
1020 err_when( ai_unit && ( !nation_recno || !nation_array[nation_recno]->is_ai() ) );
1021
1022 #else // fix bug on fly in the release version
1023
1024 if( skill.combat_level > 100 )
1025 skill.combat_level = 100;
1026
1027 #endif
1028 }
1029 //----------- End of function Unit::next_day -----------//
1030
1031
1032 //--------- Begin of function Unit::process_recover ---------//
1033 //
process_recover()1034 void Unit::process_recover()
1035 {
1036 if( hit_points==0 || hit_points == max_hit_points ) // this unit is dead already
1037 return;
1038
1039 err_when( hit_points > max_hit_points );
1040
1041 //---- overseers in firms and ships in harbors recover faster ----//
1042
1043 int hitPointsInc;
1044
1045 if( unit_mode == UNIT_MODE_OVERSEE ||
1046 unit_mode == UNIT_MODE_IN_HARBOR )
1047 {
1048 hitPointsInc = 2;
1049 }
1050
1051 //------ for units on ships --------//
1052
1053 else if( unit_mode == UNIT_MODE_ON_SHIP )
1054 {
1055 //--- if the ship where the unit is on is in the harbor, the unit recovers faster ---//
1056
1057 if( unit_array[unit_mode_para]->unit_mode == UNIT_MODE_IN_HARBOR )
1058 hitPointsInc = 2;
1059 else
1060 hitPointsInc = 1;
1061 }
1062
1063 //----- only recover when the unit is not moving -----//
1064
1065 else if( cur_action == SPRITE_IDLE )
1066 {
1067 hitPointsInc = 1;
1068 }
1069 else
1070 return;
1071
1072 //---------- recover now -----------//
1073
1074 hit_points += hitPointsInc;
1075
1076 if( hit_points > max_hit_points )
1077 hit_points = max_hit_points;
1078 }
1079 //----------- End of function Unit::process_recover -----------//
1080
1081
1082 //--------- Begin of function Unit::update_loyalty ---------//
1083 //
1084 // How loyalty of units are updated:
1085 //
1086 // General: in a military camp - updated in FirmCamp::update_loyalty()
1087 // mobile - no update
1088 //
1089 // Soldiers led by a general: in a military camp - updated in FirmCamp::update_loyalty()
1090 // mobile - updated here
1091 //
1092 // Other units: no update.
1093 //
update_loyalty()1094 void Unit::update_loyalty()
1095 {
1096 if( !nation_recno || rank_id==RANK_KING || !unit_res[unit_id]->race_id )
1097 return;
1098
1099 if( unit_mode == UNIT_MODE_CONSTRUCT ) // constructor worker will not change their loyalty when they are in a building
1100 return;
1101
1102 // The following never really worked that well, since it created a dead give away due to the constant loyalty.
1103 #ifdef EASY_SPY_DETECTION
1104 //----- if this unit is a spy, set its fake loyalty ------//
1105
1106 if( spy_recno ) // a spy's loyalty is always >= 70
1107 {
1108 if( loyalty < 70 )
1109 loyalty = 70+misc.random(20); // initialize it to be a number between 70 and 90
1110
1111 target_loyalty = loyalty;
1112 return;
1113 }
1114 #endif
1115
1116 //-------- if this is a general ---------//
1117
1118 Nation* ownNation = nation_array[nation_recno];
1119 int rc=0;
1120
1121 if( rank_id==RANK_GENERAL )
1122 {
1123 //----- the general's power affect his loyalty ----//
1124
1125 int targetLoyalty = commander_power();
1126
1127 //----- the king's race affects the general's loyalty ----//
1128
1129 if( ownNation->race_id == race_id )
1130 targetLoyalty += 20;
1131
1132 //----- the kingdom's reputation affects the general's loyalty ----//
1133
1134 targetLoyalty += (int)ownNation->reputation/4;
1135
1136 //--- the king's leadership also affect the general's loyalty -----//
1137
1138 if( ownNation->king_unit_recno )
1139 targetLoyalty += unit_array[ownNation->king_unit_recno]->skill.skill_level / 4;
1140
1141 //-- if the unit is rewarded less than the amount of contribution he made, he will become unhappy --//
1142
1143 if( nation_contribution > total_reward*2 )
1144 {
1145 int decLoyalty = (nation_contribution - total_reward*2)/2;
1146 targetLoyalty -= MIN(50, decLoyalty); // this affect 50 points at maximum
1147 }
1148
1149 targetLoyalty = MIN( targetLoyalty, 100 );
1150 target_loyalty = MAX( targetLoyalty, 0 );
1151 }
1152
1153 //-------- if this is a soldier ---------//
1154
1155 else if( rank_id==RANK_SOLDIER )
1156 {
1157 int leader_bonus = config_adv.unit_loyalty_require_local_leader ?
1158 is_leader_in_range() : leader_unit_recno;
1159 if( leader_bonus )
1160 {
1161 //----------------------------------------//
1162 //
1163 // If this soldier is led by a general,
1164 // the targeted loyalty
1165 //
1166 // = race friendliness between the unit and the general / 2
1167 // + the leader unit's leadership / 2
1168 //
1169 //----------------------------------------//
1170
1171 Unit* leaderUnit = unit_array[leader_unit_recno];
1172
1173 int targetLoyalty = 30 + leaderUnit->skill.get_skill(SKILL_LEADING);
1174
1175 //---------------------------------------------------//
1176 //
1177 // Soldiers with higher combat and leadership skill
1178 // will get discontented if they are led by a general
1179 // with low leadership.
1180 //
1181 //---------------------------------------------------//
1182
1183 targetLoyalty -= skill.combat_level/2;
1184 targetLoyalty -= skill.skill_level;
1185
1186 if( leaderUnit->rank_id == RANK_KING )
1187 targetLoyalty += 20;
1188
1189 if( race_res.is_same_race(race_id, leaderUnit->race_id) )
1190 targetLoyalty += 20;
1191
1192 if( targetLoyalty < 0 )
1193 targetLoyalty = 0;
1194
1195 targetLoyalty = MIN( targetLoyalty, 100 );
1196 target_loyalty = MAX( targetLoyalty, 0 );
1197 }
1198 else
1199 {
1200 target_loyalty = 0;
1201 }
1202 }
1203
1204 //--------- update loyalty ---------//
1205
1206 err_when( target_loyalty < 0 || target_loyalty > 100 );
1207
1208 if( target_loyalty > loyalty ) // only increase, no decrease. Decrease are caused by events. Increases are made gradually
1209 {
1210 int incValue = (target_loyalty - loyalty)/10;
1211
1212 int newLoyalty = (int) loyalty + MAX(1, incValue);
1213
1214 if( newLoyalty > target_loyalty )
1215 newLoyalty = target_loyalty;
1216
1217 loyalty = newLoyalty;
1218 }
1219 else if( target_loyalty < loyalty ) // only increase, no decrease. Decrease are caused by events. Increases are made gradually
1220 {
1221 loyalty--;
1222 }
1223
1224 err_when( loyalty < 0 || loyalty > 100 );
1225 }
1226 //-------- End of function Unit::update_loyalty -----------//
1227
1228
1229 //--------- Begin of function Unit::commander_power ---------//
1230 //
1231 // A commander's power is determined:
1232 //
1233 // -Population of the towns he controls
1234 // -The employment rate of the towns he controls, the higher the
1235 // employment rate, the higher his power is
1236 // -If there are any other commanders controls the towns at the same time.
1237 // -the no. of soldiers led by the commander and their combat levels.
1238 //
commander_power()1239 int Unit::commander_power()
1240 {
1241 //---- if the commander is in a military camp -----//
1242
1243 int commanderPower=0;
1244
1245 if( unit_mode == UNIT_MODE_OVERSEE )
1246 {
1247 Firm* firmPtr = firm_array[unit_mode_para];
1248
1249 if( firmPtr->firm_id == FIRM_CAMP )
1250 {
1251 Town* townPtr;
1252
1253 for( int i=firmPtr->linked_town_count-1 ; i>=0 ; i-- )
1254 {
1255 if( firmPtr->linked_town_enable_array[i] == LINK_EE )
1256 {
1257 townPtr = town_array[firmPtr->linked_town_array[i]];
1258
1259 commanderPower += townPtr->population / townPtr->linked_active_camp_count();
1260 }
1261 }
1262
1263 commanderPower += firmPtr->worker_count*3; // 0 to 24
1264 }
1265 else if( firmPtr->firm_id == FIRM_BASE )
1266 {
1267 commanderPower = 60;
1268 }
1269 }
1270 else
1271 {
1272 commanderPower = team_info->member_count*3; // 0 to 24
1273 }
1274
1275 return commanderPower;
1276 }
1277 //----------- End of function Unit::commander_power -----------//
1278
1279
1280 //--------- Begin of function Unit::think_betray ---------//
1281 //
think_betray()1282 int Unit::think_betray()
1283 {
1284 int unitRecno = sprite_recno;
1285
1286 err_when( unit_array.is_deleted(unitRecno) );
1287
1288 if( spy_recno ) // spies do not betray here, spy has its own functions for betrayal
1289 return 0;
1290
1291 //----- if the unit is in training or is constructing a building, do not rebel -------//
1292
1293 if( !is_visible() && unit_mode != UNIT_MODE_OVERSEE )
1294 return 0;
1295
1296 if( loyalty >= UNIT_BETRAY_LOYALTY ) // you when unit is
1297 return 0;
1298
1299 if( !unit_res[unit_id]->race_id || !nation_recno ||
1300 rank_id==RANK_KING || spy_recno )
1301 {
1302 return 0;
1303 }
1304
1305 err_when(unit_res[unit_id]->unit_class == UNIT_CLASS_GOD);
1306 err_when(unit_id==UNIT_CARAVAN);
1307
1308 //------ turn towards other nation --------//
1309
1310 int i, bestNationRecno=0, nationScore, bestScore=loyalty; // the score must be larger than the current loyalty
1311 Nation *curNation, *nationPtr;
1312 int unitRegionId = region_id();
1313
1314 if( loyalty==0 ) // if the loyalty is 0, it will definitely betray
1315 bestScore = -100;
1316
1317 curNation = nation_array[nation_recno];
1318
1319 for( i=nation_array.size() ; i>0 ; i-- )
1320 {
1321 if( nation_array.is_deleted(i) )
1322 continue;
1323
1324 if( !curNation->get_relation(i)->has_contact || i==nation_recno )
1325 continue;
1326
1327 nationPtr = nation_array[i];
1328
1329 //--- only if the nation has a base town in the region where the unit stands ---//
1330
1331 if( !region_array.nation_has_base_town(unitRegionId, i) )
1332 continue;
1333
1334 //------------------------------------------------//
1335
1336 nationScore = (int) nationPtr->reputation
1337 + (nationPtr->overall_rating - curNation->overall_rating);
1338
1339 if( race_res.is_same_race(nationPtr->race_id, race_id) )
1340 nationScore += 30;
1341
1342 if( nationScore > bestScore )
1343 {
1344 bestScore = nationScore;
1345 bestNationRecno = i;
1346 }
1347 }
1348
1349 err_when( unit_array.is_deleted(unitRecno) );
1350
1351 if( bestNationRecno )
1352 {
1353 return betray(bestNationRecno);
1354 }
1355 else if( loyalty==0 )
1356 {
1357 //----------------------------------------------//
1358 // If there is no good nation to turn towards to and
1359 // the loyalty has dropped to 0, resign itself and
1360 // leave the nation.
1361 //
1362 // However, if the unit is spy, it will stay with the
1363 // nation as it has never been really loyal to the nation.
1364 //---------------------------------------------//
1365
1366 if( rank_id != RANK_KING && is_visible() &&
1367 !spy_recno )
1368 {
1369 resign(COMMAND_AUTO);
1370 return 1;
1371 }
1372 }
1373
1374 return 0;
1375 }
1376 //-------- End of function Unit::think_betray -----------//
1377
1378
1379 //--------- Begin of function Unit::betray ---------//
1380 //
1381 // If this unit is a spy, this function betray() will be
1382 // called by Unit::spy_change_nation() or Firm::capture_firm().
1383 //
1384 // If this is not a spy, this function will only be called
1385 // by think_betray() and other nation deinit functions.
1386 //
betray(int newNationRecno)1387 int Unit::betray(int newNationRecno)
1388 {
1389 int unitRecno = sprite_recno;
1390
1391 //### begin alex 18/3 ###//
1392 //err_when( unit_array.is_deleted(unitRecno) );
1393 err_when( unit_array.is_truly_deleted(unitRecno) );
1394 //#### end alex 18/3 ####//
1395
1396 err_when( rank_id == RANK_KING );
1397
1398 if( nation_recno == newNationRecno )
1399 return 0;
1400
1401 if( unit_mode == UNIT_MODE_CONSTRUCT || // don't change nation when the unit is constructing a firm
1402 unit_mode == UNIT_MODE_ON_SHIP ) // don't change nation when the unit is constructing a firm
1403 {
1404 return 0;
1405 }
1406
1407 //---------- add news -----------//
1408
1409 if( nation_recno == nation_array.player_recno ||
1410 newNationRecno == nation_array.player_recno )
1411 {
1412 //--- if this is a spy, don't display news message for betrayal as it is already displayed in Unit::spy_change_nation() ---//
1413
1414 if( !spy_recno )
1415 news_array.unit_betray(sprite_recno, newNationRecno);
1416 }
1417
1418 //------ change nation now ------//
1419
1420 //### begin alex 18/3 ###//
1421 //err_when( unit_array.is_deleted(unitRecno) );
1422 err_when( unit_array.is_truly_deleted(unitRecno) );
1423 //#### end alex 18/3 ####//
1424
1425 change_nation(newNationRecno);
1426
1427 //### begin alex 18/3 ###//
1428 //err_when( unit_array.is_deleted(unitRecno) );
1429 err_when( unit_array.is_truly_deleted(unitRecno) );
1430 //#### end alex 18/3 ####//
1431
1432 //-------- set the loyalty of the unit -------//
1433
1434 if( nation_recno )
1435 {
1436 Nation* nationPtr = nation_array[nation_recno];
1437
1438 loyalty = UNIT_BETRAY_LOYALTY + misc.random(5);
1439
1440 if( nationPtr->reputation > 0 )
1441 change_loyalty( (int) nationPtr->reputation );
1442
1443 if( race_res.is_same_race( nationPtr->race_id, race_id ) )
1444 change_loyalty( 30 );
1445
1446 err_when( loyalty < 0 || loyalty > 100 );
1447
1448 update_loyalty(); // update target loyalty
1449 }
1450 else //------ if change to independent rebel -------//
1451 {
1452 loyalty = 0; // no loyalty needed
1453 }
1454
1455 //--- if this unit is a general, change nation for the units he commands ---//
1456
1457 uint32_t newTeamId = unit_array.cur_team_id++;
1458
1459 if( rank_id==RANK_GENERAL )
1460 {
1461 Unit* unitPtr;
1462 int i, nationReputation = (int) nation_array[nation_recno]->reputation;
1463
1464 for( i=unit_array.size() ; i>0 ; i-- )
1465 {
1466 if( unit_array.is_deleted(i) )
1467 continue;
1468
1469 unitPtr = unit_array[i];
1470
1471 //---- capture the troop this general commands -----//
1472
1473 if( unitPtr->leader_unit_recno == sprite_recno &&
1474 unitPtr->rank_id == RANK_SOLDIER && unitPtr->is_visible() )
1475 {
1476 if( unitPtr->spy_recno ) // if the unit is a spy
1477 unitPtr->spy_change_nation(newNationRecno, COMMAND_AUTO, 1); // 1-group defection of this unit, allowing us to hande the change of nation
1478
1479 unitPtr->change_nation(newNationRecno);
1480
1481 unitPtr->team_id = newTeamId; // assign new team_id or checking for nation_recno
1482 }
1483 }
1484 }
1485
1486 team_id = newTeamId;
1487
1488 //### begin alex 18/3 ###//
1489 //err_when( unit_array.is_deleted(unitRecno) );
1490 err_when( unit_array.is_truly_deleted(unitRecno) );
1491 //#### end alex 18/3 ####//
1492
1493 //------ go to meet the new master -------//
1494
1495 if( is_visible() && nation_recno )
1496 {
1497 if( !spy_recno || spy_array[spy_recno]->notify_cloaked_nation_flag )
1498 {
1499 if( rank_id == RANK_GENERAL ) // generals shouldn't automatically be assigned to camps, they should just move near your villages
1500 ai_move_to_nearby_town();
1501 else
1502 think_normal_human_action(); // this is an AI function in OUNITAI.CPP
1503 }
1504 }
1505
1506 //### begin alex 18/3 ###//
1507 //err_when( unit_array.is_deleted(unitRecno) );
1508 err_when( unit_array.is_truly_deleted(unitRecno) );
1509 //#### end alex 18/3 ####//
1510
1511 return 1;
1512 }
1513 //-------- End of function Unit::betray -----------//
1514
1515
1516 //--------- Begin of function Unit::change_nation ---------//
1517 //
1518 // This function is called when a unit change nation.
1519 // It is not necessarily a result of betray, when a spy
1520 // hands over his new nation to his parent nation, this
1521 // function will also be called.
1522 //
1523 // <int> newNationRecno - change the nation of the unit.
1524 //
change_nation(int newNationRecno)1525 void Unit::change_nation(int newNationRecno)
1526 {
1527 err_when( newNationRecno && nation_array.is_deleted(newNationRecno) );
1528 err_when( unit_mode == UNIT_MODE_REBEL ); // rebels do not change nation
1529
1530 //---------------------------------//
1531
1532 int oldAiUnit = ai_unit;
1533 int oldNationRecno = nation_recno;
1534
1535 group_select_id = 0; // clear group select id
1536 if(way_point_count)
1537 reset_way_point_array();
1538
1539 //-- if the player is giving a command to this unit, cancel the command --//
1540
1541 if( nation_recno == nation_array.player_recno &&
1542 sprite_recno == unit_array.selected_recno &&
1543 power.command_id )
1544 {
1545 power.command_id = 0;
1546 }
1547
1548 //---------- stop all action to attack this unit ------------//
1549
1550 unit_array.stop_attack_unit(sprite_recno);
1551
1552 //---- update nation_unit_count_array[] ----//
1553
1554 unit_res[unit_id]->unit_change_nation(newNationRecno, nation_recno, rank_id);
1555
1556 //------- if the nation has an AI action -------//
1557
1558 stop2(); // clear the existing order
1559
1560 //---------------- update vars ----------------//
1561
1562 unit_group_id = unit_array.cur_group_id++; // separate from the current group
1563 nation_recno = newNationRecno;
1564
1565 home_camp_firm_recno = 0; // reset it
1566 original_action_mode = 0;
1567
1568 if( race_id )
1569 {
1570 nation_contribution = 0; // contribution to the nation
1571 total_reward = 0;
1572 }
1573
1574 //-------- if change to one of the existing nations ------//
1575
1576 ai_unit = nation_recno && nation_array[nation_recno]->is_ai();
1577
1578 //------------ update AI info --------------//
1579
1580 if( oldAiUnit )
1581 {
1582 Nation* nationPtr = nation_array[oldNationRecno];
1583
1584 if( rank_id == RANK_GENERAL || rank_id == RANK_KING )
1585 nationPtr->del_general_info(sprite_recno);
1586
1587 else if( unit_res[unit_id]->unit_class == UNIT_CLASS_CARAVAN )
1588 nationPtr->del_caravan_info(sprite_recno);
1589
1590 else if( unit_res[unit_id]->unit_class == UNIT_CLASS_SHIP )
1591 nationPtr->del_ship_info(sprite_recno);
1592 }
1593
1594 if( ai_unit && nation_recno != 0 )
1595 {
1596 Nation* nationPtr = nation_array[nation_recno];
1597
1598 if( rank_id == RANK_GENERAL || rank_id == RANK_KING )
1599 nationPtr->add_general_info(sprite_recno);
1600
1601 else if( unit_res[unit_id]->unit_class == UNIT_CLASS_CARAVAN )
1602 nationPtr->add_caravan_info(sprite_recno);
1603
1604 else if( unit_res[unit_id]->unit_class == UNIT_CLASS_SHIP )
1605 nationPtr->add_ship_info(sprite_recno);
1606 }
1607
1608 //------ if this unit oversees a firm -----//
1609
1610 if( unit_mode==UNIT_MODE_OVERSEE )
1611 firm_array[unit_mode_para]->change_nation(newNationRecno);
1612
1613 //----- this unit was defending the town before it gets killed ----//
1614
1615 else if( unit_mode==UNIT_MODE_DEFEND_TOWN )
1616 {
1617 if( !town_array.is_deleted(unit_mode_para) )
1618 town_array[unit_mode_para]->reduce_defender_count();
1619
1620 set_mode(0); // reset unit mode
1621 }
1622
1623 //---- if the unit is no longer the same nation as the leader ----//
1624
1625 if( leader_unit_recno )
1626 {
1627 Unit* leaderUnit = unit_array[leader_unit_recno];
1628
1629 if( leaderUnit->nation_recno != nation_recno )
1630 {
1631 leaderUnit->del_team_member(sprite_recno);
1632 leader_unit_recno = 0;
1633 team_id = 0;
1634 }
1635 }
1636
1637 //------ if it is currently selected -------//
1638
1639 if( selected_flag )
1640 info.disp();
1641 }
1642 //----------- End of function Unit::change_nation -----------//
1643
1644
1645 //--------- Begin of function Unit::pay_expense ---------//
1646 //
pay_expense()1647 void Unit::pay_expense()
1648 {
1649 if( game.game_mode == GAME_TEST ) // no deduction in testing game
1650 return;
1651
1652 if( !nation_recno )
1653 return;
1654
1655 //--- if it's a mobile spy or the spy is in its own firm, no need to pay salary here as Spy::pay_expense() will do that ---//
1656 //
1657 // -If your spies are mobile:
1658 // >your nation pays them 1 food and $5 dollars per month
1659 //
1660 // -If your spies are in an enemy's town or firm:
1661 // >the enemy pays them 1 food and the normal salary of their jobs.
1662 //
1663 // >your nation pays them $5 dollars per month. (your nation pays them no food)
1664 //
1665 // -If your spies are in your own town or firm:
1666 // >your nation pays them 1 food and $5 dollars per month
1667 //
1668 //------------------------------------------------------//
1669
1670 if( spy_recno )
1671 {
1672 if( is_visible() ) // the cost will be deducted in spy_array
1673 return;
1674
1675 if( unit_mode == UNIT_MODE_OVERSEE &&
1676 firm_array[unit_mode_para]->nation_recno == true_nation_recno() )
1677 {
1678 return;
1679 }
1680 }
1681
1682 //---------- if it's a human unit -----------//
1683 //
1684 // The unit is paid even during its training period in a town
1685 //
1686 //-------------------------------------------//
1687
1688 Nation* nationPtr = nation_array[nation_recno];
1689
1690 if( unit_res[unit_id]->race_id )
1691 {
1692 //---------- reduce cash -----------//
1693
1694 if( nationPtr->cash > 0 )
1695 {
1696 if( rank_id == RANK_SOLDIER )
1697 nationPtr->add_expense( EXPENSE_MOBILE_UNIT, (float) SOLDIER_YEAR_SALARY / 365, 1 );
1698
1699 if( rank_id == RANK_GENERAL )
1700 nationPtr->add_expense( EXPENSE_GENERAL, (float) GENERAL_YEAR_SALARY / 365, 1 );
1701 }
1702 else // decrease loyalty if the nation cannot pay the unit
1703 {
1704 change_loyalty(-1);
1705 }
1706
1707 //---------- reduce food -----------//
1708
1709 if( unit_res[unit_id]->race_id ) // if it's a human unit
1710 {
1711 if( nationPtr->food > 0 )
1712 nationPtr->consume_food((float) PERSON_FOOD_YEAR_CONSUMPTION / 365);
1713 else
1714 {
1715 if( info.game_date%NO_FOOD_LOYALTY_DECREASE_INTERVAL == 0 ) // decrease 1 loyalty point every 2 days
1716 change_loyalty(-1);
1717 }
1718 }
1719 }
1720 else //----- it's a non-human unit ------//
1721 {
1722 if( nationPtr->cash > 0 )
1723 {
1724 int expenseType;
1725
1726 switch(unit_res[unit_id]->unit_class)
1727 {
1728 case UNIT_CLASS_WEAPON:
1729 expenseType = EXPENSE_WEAPON;
1730 break;
1731
1732 case UNIT_CLASS_SHIP:
1733 expenseType = EXPENSE_SHIP;
1734 break;
1735
1736 case UNIT_CLASS_CARAVAN:
1737 expenseType = EXPENSE_CARAVAN;
1738 break;
1739
1740 default:
1741 expenseType = EXPENSE_MOBILE_UNIT;
1742 }
1743
1744 nationPtr->add_expense( expenseType, (float) unit_res[unit_id]->year_cost / 365, 1 );
1745 }
1746 else // decrease hit points if the nation cannot pay the unit
1747 {
1748 if( unit_res[unit_id]->unit_class != UNIT_CLASS_CARAVAN ) // Even when caravans are not paid, they still stay in your service.
1749 {
1750 if( hit_points > 0 )
1751 {
1752 hit_points--;
1753
1754 if( hit_points < 0 )
1755 hit_points = (float) 0;
1756
1757 //--- when the hit points drop to zero and the unit is destroyed ---//
1758
1759 if( hit_points==0 )
1760 {
1761 if( nation_recno == nation_array.player_recno )
1762 {
1763 int unitClass = unit_res[unit_id]->unit_class;
1764
1765 if( unitClass==UNIT_CLASS_WEAPON )
1766 news_array.weapon_ship_worn_out(unit_id, get_weapon_version());
1767
1768 else if( unitClass==UNIT_CLASS_SHIP )
1769 news_array.weapon_ship_worn_out(unit_id, 0);
1770 }
1771 }
1772 }
1773 }
1774 }
1775 }
1776 }
1777 //----------- End of function Unit::pay_expense -----------//
1778
1779
1780 //--------- Begin of function Unit::change_hit_points ---------//
1781 //
change_hit_points(float changePoints)1782 void Unit::change_hit_points(float changePoints)
1783 {
1784 hit_points += changePoints;
1785
1786 if( hit_points < 0 )
1787 hit_points = (float) 0;
1788
1789 if( hit_points > max_hit_points )
1790 hit_points = max_hit_points;
1791 }
1792 //-------- End of function Unit::change_hit_points -----------//
1793
1794
1795 //--------- Begin of function Unit::change_loyalty ---------//
1796 //
1797 // <int> changeAmt - amount of loyalty to be changed.
1798 //
change_loyalty(int changeAmt)1799 void Unit::change_loyalty(int changeAmt)
1800 {
1801 int newLoyalty = loyalty + changeAmt;
1802
1803 newLoyalty = MAX(0, newLoyalty);
1804
1805 loyalty = MIN(100, newLoyalty);
1806 }
1807 //----------- End of function Unit::change_loyalty -----------//
1808
1809
1810 //------- Begin of function Unit::inc_minor_combat_level --------//
1811 //
inc_minor_combat_level(int incLevel)1812 void Unit::inc_minor_combat_level(int incLevel)
1813 {
1814 err_when( incLevel<0 || incLevel>100 ); // it cannot be larger than 100, because the current code can't handle it
1815
1816 skill.combat_level_minor += incLevel;
1817
1818 if( skill.combat_level_minor > 100 )
1819 {
1820 if( skill.combat_level < 100 )
1821 set_combat_level(skill.combat_level+1);
1822
1823 skill.combat_level_minor -= 100;
1824 }
1825 }
1826 //-------- End of function Unit::inc_minor_combat_level ---------//
1827
1828
1829 //------- Begin of function Unit::inc_minor_skill_level --------//
1830 //
inc_minor_skill_level(int incLevel)1831 void Unit::inc_minor_skill_level(int incLevel)
1832 {
1833 err_when( incLevel<0 || incLevel>100 );
1834
1835 skill.skill_level_minor += incLevel;
1836
1837 if( skill.skill_level_minor > 100 )
1838 {
1839 if( skill.skill_level < 100 )
1840 skill.skill_level++;
1841
1842 skill.skill_level_minor -= 100;
1843 }
1844 }
1845 //-------- End of function Unit::inc_minor_skill_level ---------//
1846
1847
1848 //--------- Begin of function Unit::set_combat_level ---------//
1849 //
set_combat_level(int combatLevel)1850 void Unit::set_combat_level(int combatLevel)
1851 {
1852 err_when( combatLevel<=0 || combatLevel>100 );
1853
1854 skill.combat_level = combatLevel;
1855
1856 UnitInfo* unitInfo = unit_res[unit_id];
1857
1858 int oldMaxHitPoints = max_hit_points;
1859
1860 max_hit_points = unitInfo->hit_points * combatLevel / 100;
1861
1862 hit_points = hit_points * max_hit_points / oldMaxHitPoints;
1863
1864 hit_points = MIN(hit_points, max_hit_points);
1865
1866 // --------- update can_guard_flag -------//
1867
1868 if( combatLevel >= unitInfo->guard_combat_level)
1869 {
1870 can_guard_flag = sprite_info->can_guard_flag;
1871 if( unit_id == UNIT_ZULU )
1872 can_guard_flag |= 4; // shield during attack delay
1873 }
1874 else
1875 {
1876 can_guard_flag = 0;
1877 }
1878
1879 max_power = skill.combat_level + 50;
1880 cur_power = MIN(cur_power, max_power);
1881 }
1882 //-------- End of function Unit::set_combat_level -----------//
1883
1884
1885 //--------- Begin of function Unit::set_rank ---------//
1886 //
1887 // Only if the unit has leadership skill, it can be a general or king.
1888 //
set_rank(int rankId)1889 void Unit::set_rank(int rankId)
1890 {
1891 err_when( !race_id );
1892
1893 #ifdef DEBUG
1894 if( !is_visible() )
1895 {
1896 err_when( rank_id==RANK_GENERAL && rankId==RANK_SOLDIER );
1897 err_when( rank_id==RANK_SOLDIER && rankId==RANK_GENERAL );
1898 }
1899 #endif
1900
1901 if( rank_id == rankId )
1902 return;
1903
1904 //------- promote --------//
1905
1906 if( rankId > rank_id )
1907 change_loyalty(PROMOTE_LOYALTY_INCREASE);
1908
1909 //------- demote -----------//
1910
1911 else if( rankId < rank_id && rank_id != RANK_KING ) // no decrease in loyalty if a spy king hands his nation to his parent nation and become a general again
1912 change_loyalty(-DEMOTE_LOYALTY_DECREASE);
1913
1914 //---- update nation_general_count_array[] ----//
1915
1916 if( nation_recno )
1917 {
1918 UnitInfo* unitInfo = unit_res[unit_id];
1919
1920 if( rank_id == RANK_GENERAL ) // if it was a general originally
1921 unitInfo->dec_nation_general_count(nation_recno);
1922
1923 if( rankId == RANK_GENERAL ) // if the new rank is general
1924 unitInfo->inc_nation_general_count(nation_recno);
1925
1926 //------ if demote a king to a unit ------//
1927
1928 if( rank_id == RANK_KING && rankId != RANK_KING )
1929 unitInfo->inc_nation_unit_count(nation_recno); // since kings are not included in nation_unit_count, when it is no longer a king, we need to re-increase it.
1930
1931 //------ if promote a unit to a king ------//
1932
1933 if( rank_id != RANK_KING && rankId == RANK_KING )
1934 unitInfo->dec_nation_unit_count(nation_recno); // since kings are not included in nation_unit_count, we need to decrease it
1935 }
1936
1937 //----- reset leader_unit_recno if demote a general to soldier ----//
1938
1939 if( rank_id == RANK_GENERAL && rankId == RANK_SOLDIER )
1940 {
1941 //----- reset leader_unit_recno of the units he commands ----//
1942
1943 for( int i=unit_array.size() ; i>0 ; i-- )
1944 {
1945 Unit* unitPtr = (Unit*) unit_array.get_ptr(i); // don't use is_deleted() as it filters out units that are currently dying
1946
1947 if( unitPtr && unitPtr->leader_unit_recno == sprite_recno )
1948 {
1949 unitPtr->leader_unit_recno = 0;
1950 unitPtr->team_id = 0;
1951 }
1952 }
1953
1954 //--------- deinit team_info ---------//
1955
1956 err_when( !team_info );
1957
1958 mem_del(team_info);
1959 team_info = NULL;
1960 team_id = 0;
1961 }
1962
1963 //----- if this is a soldier being promoted to a general -----//
1964
1965 else if( rank_id == RANK_SOLDIER && rankId == RANK_GENERAL )
1966 {
1967 //-- if this soldier is formerly commanded by a general, detech it ---//
1968
1969 if( leader_unit_recno )
1970 {
1971 if( !unit_array.is_deleted(leader_unit_recno) ) // the leader unit may have been killed at the same time
1972 unit_array[leader_unit_recno]->del_team_member(sprite_recno);
1973
1974 leader_unit_recno = 0;
1975 }
1976 }
1977
1978 //-------------- update AI info --------------//
1979
1980 if( ai_unit )
1981 {
1982 if( rank_id == RANK_GENERAL || rank_id == RANK_KING )
1983 nation_array[nation_recno]->del_general_info(sprite_recno);
1984
1985 rank_id = rankId;
1986
1987 if( rank_id == RANK_GENERAL || rank_id == RANK_KING )
1988 nation_array[nation_recno]->add_general_info(sprite_recno);
1989 }
1990 else
1991 {
1992 rank_id = rankId;
1993 }
1994
1995 //----- if this is a general/king ------//
1996
1997 if( rank_id == RANK_GENERAL || rank_id == RANK_KING )
1998 {
1999 //--------- init team_info -------//
2000
2001 if( !team_info )
2002 {
2003 team_info = (TeamInfo*) mem_add( sizeof(TeamInfo) );
2004 team_info->member_count = 0;
2005 team_info->ai_last_request_defense_date = 0;
2006 }
2007
2008 //--- set leadership if this unit does not have any now ----//
2009
2010 if( skill.skill_id != SKILL_LEADING )
2011 {
2012 skill.skill_id = SKILL_LEADING;
2013 skill.skill_level = 10 + misc.random(40);
2014 }
2015 }
2016
2017 //------ refresh if the current unit is selected -----//
2018
2019 if( unit_array.selected_recno == sprite_recno )
2020 info.disp();
2021 }
2022 //-------- End of function Unit::set_rank -----------//
2023
2024
2025 //--------- Begin of function Unit::embark ---------//
2026 //
2027 // Order this unit to embark an vehicle
2028 //
2029 // <int> vehicleRecno - recno of the vehicle.
2030 //
embark(int vehicleRecno)2031 void Unit::embark(int vehicleRecno)
2032 {
2033 err_here(); // this function is no longer functional
2034
2035 Unit* vehiclePtr = unit_array[vehicleRecno];
2036
2037 if( unit_res[unit_id]->vehicle_id == vehiclePtr->unit_id ) // not the right vehicle
2038 return;
2039
2040 int vehicleUnitId = unit_res[unit_id]->vehicle_unit_id;
2041 float vehicleHitPoints = vehiclePtr->hit_points;
2042 int xLoc = vehiclePtr->cur_x_loc();
2043 int yLoc = vehiclePtr->cur_y_loc();
2044
2045 //------- delete the vehicle unit --------//
2046
2047 unit_array.del(vehicleRecno); // delete the vehicle (e.g. horse)
2048
2049 //--------- add the combined unit -------//
2050
2051 int newUnitRecno = unit_array.add_unit(vehicleUnitId, nation_recno, rank_id, loyalty, xLoc, yLoc); // add the combined unit (e.g. cavalry)
2052
2053 UnitVehicle* unitVehicle = (UnitVehicle*) unit_array[newUnitRecno];
2054
2055 unitVehicle->skill = skill;
2056 unitVehicle->set_combat_level(skill.combat_level);
2057
2058 unitVehicle->solider_hit_points = (int) hit_points;
2059 unitVehicle->vehicle_hit_points = (int) vehicleHitPoints;
2060
2061 unitVehicle->hit_points = (float) unitVehicle->solider_hit_points +
2062 unitVehicle->vehicle_hit_points;
2063
2064 //-------- delete the solider unit ---------//
2065
2066 unit_array.del(sprite_recno); // delete the embarker (e.g. knight)
2067 }
2068 //-------- End of function Unit::embark -----------//
2069
2070
2071
2072 //--------- Begin of function Unit::reward ---------//
2073 //
2074 // <int> rewardNationRecno - the nation which does this reward.
2075 //
reward(int rewardNationRecno)2076 void Unit::reward(int rewardNationRecno)
2077 {
2078 // ###### patch begin Gilbert 24/9 ########//
2079 if( nation_array[rewardNationRecno]->cash < REWARD_COST )
2080 return;
2081 // ###### patch end Gilbert 24/9 ########//
2082
2083 //--------- if this is a spy ---------//
2084
2085 if( spy_recno && true_nation_recno() == rewardNationRecno ) // if the spy's owning nation rewards the spy
2086 {
2087 spy_array[spy_recno]->change_loyalty(REWARD_LOYALTY_INCREASE);
2088 }
2089
2090 //--- if this spy's nation_recno & true_nation_recno() are both == rewardNationRecno, it's true loyalty and cloaked loyalty will both be increased ---//
2091
2092 if( nation_recno == rewardNationRecno )
2093 {
2094 total_reward += REWARD_COST;
2095
2096 change_loyalty(REWARD_LOYALTY_INCREASE);
2097 }
2098
2099 nation_array[rewardNationRecno]->add_expense(EXPENSE_REWARD_UNIT, (float)REWARD_COST);
2100 }
2101 //----------- End of function Unit::reward -----------//
2102
2103
2104 //------- Begin of function Unit::overseer_migrate ---------//
2105 //
2106 // Order the overseer migrate to a new town but still keeps
2107 // working for the same firm.
2108 //
2109 // <int> destTownRecno - the recno of the town the worker should
2110 // migrate to.
2111 //
overseer_migrate(int destTownRecno)2112 void Unit::overseer_migrate(int destTownRecno)
2113 {
2114 err_when( unit_mode!=UNIT_MODE_OVERSEE );
2115
2116 int curTownRecno = firm_array[unit_mode_para]->overseer_town_recno;
2117
2118 //------- decrease the population of the unit's home town ------//
2119
2120 town_array[curTownRecno]->dec_pop(race_id, 1);
2121
2122 //--------- increase the population of the target town ------//
2123
2124 town_array[destTownRecno]->inc_pop(race_id, 1, loyalty );
2125 }
2126 //-------- End of function Unit::overseer_migrate ---------//
2127
2128
2129 //--------- Begin of function Unit::group_transform ---------//
2130
group_transform(char remoteAction,short * selectedArray,short selectedCount)2131 void Unit::group_transform(char remoteAction, short *selectedArray, short selectedCount)
2132 {
2133 }
2134 //----------- End of function Unit::group_transform -----------//
2135
2136
2137 //--------- Begin of function Unit::transform ---------//
2138 //
2139 // Transform the unit into another unit type.
2140 //
transform()2141 void Unit::transform()
2142 {
2143 /*
2144 UnitInfo* unitInfo = unit_res[unit_id];
2145
2146 if( unitInfo->transform_unit_id==0 )
2147 return;
2148
2149 UnitInfo* newUnitInfo = unit_res[unitInfo->transform_unit_id];
2150
2151 //--- check if the unit has the required combat level ---//
2152
2153 if( skill.combat_level < unitInfo->transform_combat_level )
2154 return;
2155
2156 //------ free up space on the map for the new unit -----//
2157
2158 int xLoc=next_x_loc(), yLoc=next_y_loc();
2159
2160 stop();
2161
2162 deinit_sprite(1); // 1-keep the unit selected if it is currently selected
2163
2164 //---- locate the space for the new unit as their size, mobile type may be different ----//
2165
2166 SpriteInfo* spriteInfo = sprite_res[newUnitInfo->sprite_id];
2167 int xLoc2 = xLoc + spriteInfo->loc_width-1;
2168 int yLoc2 = yLoc + spriteInfo->loc_height-1;
2169
2170 if( !world.check_unit_space(xLoc, yLoc, xLoc2, yLoc2, newUnitInfo->mobile_type) ) // first check if if is free to create unit on the same location
2171 {
2172 if( !world.locate_space( xLoc, yLoc, xLoc+spriteInfo->loc_width-1,
2173 yLoc+spriteInfo->loc_height-1, spriteInfo->loc_width,
2174 spriteInfo->loc_height, newUnitInfo->mobile_type) )
2175 {
2176 init_sprite( xLoc, yLoc ); // not able to find space for the new unit, transformation cancelled.
2177 return;
2178 }
2179 }
2180
2181 //--------- transform now ------------//
2182
2183 deinit_unit_id();
2184
2185 init_unit_id(unitInfo->transform_unit_id);
2186
2187 init_sprite( xLoc, yLoc );
2188 */
2189 }
2190 //----------- End of function Unit::transform -----------//
2191
2192
2193 //--------- Begin of function Unit::spy_change_nation ---------//
2194 //
2195 // Change the deceiving nation recno of a spy unit which you control.
2196 //
2197 // <int> newNationRecno - the new nation the spy changes its cloack to
2198 // <char> remoteAction - remote action type
2199 // <int> groupDefect - if 1 this spy changed nation as part of a group defect called by Unit::betray
2200 // when a General-spy changes colour and takes its subordinates along
2201 // and the subordinate is actually an enemy spy. Supresses news notification and
2202 // lets the caller handle the Unit nation change (so only handles spy nation change)
2203 //
spy_change_nation(int newNationRecno,char remoteAction,int groupDefect)2204 void Unit::spy_change_nation(int newNationRecno, char remoteAction, int groupDefect)
2205 {
2206 if( newNationRecno == nation_recno )
2207 return;
2208
2209 if( newNationRecno && nation_array.is_deleted(newNationRecno) ) // this can happen in a multiplayer message
2210 return;
2211
2212 //------- if this is a remote action -------//
2213
2214 if( !remoteAction && remote.is_enable() )
2215 {
2216 // packet structure <unit recno> <new nation Recno> <group defect>
2217 short *shortPtr = (short *)remote.new_send_queue_msg(MSG_UNIT_SPY_NATION, 3*sizeof(short) );
2218 *shortPtr = sprite_recno;
2219 shortPtr[1] = newNationRecno;
2220 shortPtr[2] = groupDefect;
2221 return;
2222 }
2223
2224 //----- update the var in Spy ------//
2225
2226 Spy* spyPtr = spy_array[spy_recno];
2227
2228 //--- when a spy change cloak to another nation, he can't cloak as a general, he must become a soldier first ---//
2229
2230 if( is_visible() && // if the spy is a commander in a camp, don't set its rank to soldier
2231 rank_id == RANK_GENERAL &&
2232 newNationRecno != spyPtr->true_nation_recno )
2233 {
2234 set_rank(RANK_SOLDIER);
2235 }
2236
2237 //---------------------------------------------------//
2238 //
2239 // If this spy unit is a general or an overseer of the
2240 // cloaked nation, when he changes nation, that will
2241 // inevitably be noticed by the cloaked nation.
2242 //
2243 //---------------------------------------------------//
2244
2245 if( spyPtr->true_nation_recno != nation_array.player_recno ) // only send news message if he is not the player's own spy
2246 {
2247 if( rank_id == RANK_GENERAL || unit_mode == UNIT_MODE_OVERSEE ||
2248 (spyPtr->notify_cloaked_nation_flag && !groupDefect) )
2249 {
2250 //-- if this spy's cloaked nation is the player's nation, the player will be notified --//
2251
2252 if( nation_recno == nation_array.player_recno )
2253 news_array.unit_betray(sprite_recno, newNationRecno);
2254 }
2255
2256 //---- send news to the cloaked nation if notify flag is on ---//
2257
2258 if( spyPtr->notify_cloaked_nation_flag && !groupDefect )
2259 {
2260 if( newNationRecno == nation_array.player_recno ) // cloaked as the player's nation
2261 news_array.unit_betray(sprite_recno, newNationRecno);
2262 }
2263 }
2264
2265 //--------- change nation recno now --------//
2266
2267 spyPtr->cloaked_nation_recno = newNationRecno;
2268
2269 if (!groupDefect)
2270 betray(newNationRecno); // call the betray function to change natino. There is no difference between a spy changing nation and a unit truly betrays
2271 }
2272 //----------- End of function Unit::spy_change_nation -----------//
2273
2274
2275 //--------- Begin of function Unit::can_spy_change_nation ---------//
2276 //
2277 // Whether the spy unit can change its spy cloak now or not.
2278 //
2279 // If there are enemy nearby, the unit cannot change its cloak.
2280 //
can_spy_change_nation()2281 int Unit::can_spy_change_nation()
2282 {
2283 if( !spy_recno )
2284 return 0;
2285
2286 //--------------------------------------------//
2287
2288 int xLoc1=cur_x_loc()-SPY_ENEMY_RANGE, yLoc1=cur_y_loc()-SPY_ENEMY_RANGE;
2289 int xLoc2=cur_x_loc()+SPY_ENEMY_RANGE, yLoc2=cur_y_loc()+SPY_ENEMY_RANGE;
2290
2291 xLoc1 = MAX(0, xLoc1);
2292 yLoc1 = MAX(0, yLoc1);
2293 xLoc2 = MIN(MAX_WORLD_X_LOC-1, xLoc2);
2294 yLoc2 = MIN(MAX_WORLD_Y_LOC-1, yLoc2);
2295
2296 int xLoc, yLoc;
2297 int unitRecno, trueNationRecno = true_nation_recno();
2298 Location* locPtr;
2299
2300 for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
2301 {
2302 locPtr = world.get_loc(xLoc1, yLoc);
2303
2304 for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
2305 {
2306 if( locPtr->has_unit(UNIT_LAND) )
2307 unitRecno = locPtr->unit_recno(UNIT_LAND);
2308
2309 else if( locPtr->has_unit(UNIT_SEA) )
2310 unitRecno = locPtr->unit_recno(UNIT_SEA);
2311
2312 else if( locPtr->has_unit(UNIT_AIR) )
2313 unitRecno = locPtr->unit_recno(UNIT_AIR);
2314
2315 else
2316 continue;
2317
2318 if( unit_array.is_deleted(unitRecno) ) // the unit is dying, its recno is still in the location
2319 continue;
2320
2321 if( unit_array[unitRecno]->true_nation_recno() != trueNationRecno )
2322 return 0;
2323 }
2324 }
2325
2326 return 1;
2327 }
2328 //----------- End of function Unit::can_spy_change_nation -----------//
2329
2330
2331 //--------- Begin of function Unit::resign ---------//
2332 //
2333 // Resign the unit.
2334 //
resign(int remoteAction)2335 void Unit::resign(int remoteAction)
2336 {
2337 if( !remoteAction && remote.is_enable() )
2338 {
2339 // packet structure : <unit recno> <nation recno>
2340 short *shortPtr = (short *)remote.new_send_queue_msg(MSG_UNIT_RESIGN, 2*sizeof(short));
2341 *shortPtr = sprite_recno;
2342 shortPtr[1] = nation_array.player_recno;
2343
2344 return;
2345 }
2346
2347 //---- increase the wandering count when a unit is disbanded ----//
2348
2349 if( race_id )
2350 town_array.race_wander_pop_array[race_id-1] += 2; // disbanding one resulted in two wandering units to make the effect more significant
2351
2352 //--- if the unit is visible, call stop2() so if it has an AI action queue, that will be reset ---//
2353
2354 if( is_visible() )
2355 stop2();
2356
2357 //--- if the spy is resigned by an enemy, display a message ---//
2358
2359 if( spy_recno && true_nation_recno() != nation_recno ) // the spy is cloaked in an enemy nation when it is resigned
2360 {
2361 //------ decrease reputation ------//
2362
2363 nation_array[true_nation_recno()]->change_reputation((float)-SPY_KILLED_REPUTATION_DECREASE);
2364
2365 //------- add news message -------//
2366
2367 if( true_nation_recno() == nation_array.player_recno || // display when the player's spy is revealed or the player has revealed an enemy spy
2368 nation_recno == nation_array.player_recno )
2369 {
2370 //--- If a spy is caught, the spy's nation's reputation wil decrease ---//
2371
2372 news_array.spy_killed(spy_recno);
2373 }
2374 }
2375
2376 //----------------------------------------------//
2377
2378 if( rank_id == RANK_GENERAL ) // if this is a general, news_array.general_die() will be called, set news_add_flag to 0 to suppress the display of thew news
2379 news_array.news_add_flag=0;
2380
2381 unit_array.del( sprite_recno );
2382
2383 news_array.news_add_flag=1;
2384 }
2385 //----------- End of function Unit::resign -----------//
2386
2387
2388 //--------- Begin of function Unit::region_id ---------//
2389 //
2390 // Return the region id. of this unit.
2391 //
region_id()2392 uint8_t Unit::region_id()
2393 {
2394 if( is_visible() )
2395 {
2396 return world.get_region_id( next_x_loc(), next_y_loc() );
2397 }
2398 else
2399 {
2400 if( unit_mode == UNIT_MODE_OVERSEE )
2401 return firm_array[unit_mode_para]->region_id;
2402 }
2403
2404 return 0;
2405 }
2406 //----------- End of function Unit::region_id -----------//
2407
2408
2409 //--------- Begin of function Unit::del_team_member ---------//
2410 //
2411 // Delete a specific member of the team led by this leader.
2412 //
del_team_member(int unitRecno)2413 void Unit::del_team_member(int unitRecno)
2414 {
2415 err_when( !team_info );
2416
2417 for( int i=0 ; i<team_info->member_count ; i++ )
2418 {
2419 if( team_info->member_unit_array[i] == unitRecno )
2420 {
2421 err_when( team_info->member_count > MAX_TEAM_MEMBER );
2422
2423 misc.del_array_rec( team_info->member_unit_array, team_info->member_count,
2424 sizeof( team_info->member_unit_array[0] ), i+1 );
2425
2426 team_info->member_count--;
2427 return;
2428 }
2429 }
2430
2431 //-------------------------------------------------------//
2432 //
2433 // Note: for rebels and monsters, although they have
2434 // leader_unit_recno, their team_info is not used.
2435 // So del_team_member() won't be able to match the
2436 // unit in its member_unit_array[].
2437 //
2438 //-------------------------------------------------------//
2439 }
2440 //----------- End of function Unit::del_team_member -----------//
2441
2442
2443 //--------- Begin of function Unit::validate_team ---------//
2444 //
2445 // Validate the member in this commander's team. If there
2446 // are any units with hit_points <= 0, delete them.
2447 //
2448 // Those unit may just be killed, so soon that the Unit's set_die()
2449 // function hsa been called yet. validate_team() function must
2450 // be called before all issunig any new team actions.
2451 //
validate_team()2452 void Unit::validate_team()
2453 {
2454 err_when( !team_info );
2455
2456 int unitRecno;
2457
2458 for( int i=team_info->member_count-1 ; i>=0 ; i-- )
2459 {
2460 unitRecno = team_info->member_unit_array[i];
2461
2462 if( unit_array.is_deleted(unitRecno) )
2463 {
2464 err_when( team_info->member_count > MAX_TEAM_MEMBER );
2465
2466 misc.del_array_rec( team_info->member_unit_array, team_info->member_count,
2467 sizeof( team_info->member_unit_array[0] ), i+1 );
2468
2469 team_info->member_count--;
2470 }
2471 }
2472 }
2473 //----------- End of function Unit::validate_team -----------//
2474
2475
2476 //--------- Begin of function Unit::commanded_soldier_count ---------//
2477 //
2478 // Return the no. of soldiers commanded by this unit.
2479 //
commanded_soldier_count()2480 int Unit::commanded_soldier_count()
2481 {
2482 if( rank_id != RANK_GENERAL && rank_id != RANK_KING )
2483 return 0;
2484
2485 //--------------------------------------//
2486
2487 err_when( !team_info );
2488
2489 int soldierCount=0;
2490
2491 if( is_visible() )
2492 {
2493 soldierCount = team_info->member_count-1;
2494
2495 if( soldierCount < 0 ) // member_count can be 0
2496 soldierCount = 0;
2497 }
2498 else
2499 {
2500 if( unit_mode == UNIT_MODE_OVERSEE )
2501 {
2502 Firm* firmPtr = firm_array[unit_mode_para];
2503
2504 if( firmPtr->firm_id == FIRM_CAMP ) // it can be an overseer of a seat of powre
2505 soldierCount = firmPtr->worker_count;
2506 }
2507 }
2508
2509 return soldierCount;
2510 }
2511 //----------- End of function Unit::commanded_soldier_count -----------//
2512
2513
2514 //----------- Begin of function Unit::fix_attack_info -----------//
fix_attack_info()2515 void Unit::fix_attack_info()
2516 {
2517 int techLevel;
2518 UnitInfo *unitInfo = unit_res[unit_id];
2519
2520 attack_count = unitInfo->attack_count;
2521
2522 if( attack_count > 0 && unitInfo->first_attack > 0)
2523 attack_info_array = unit_res.attack_info_array+unitInfo->first_attack-1;
2524 else
2525 attack_info_array = NULL;
2526
2527 if( unitInfo->unit_class == UNIT_CLASS_WEAPON &&
2528 (techLevel=get_weapon_version()) > 0 )
2529 {
2530 switch( unit_id )
2531 {
2532 case UNIT_BALLISTA:
2533 case UNIT_F_BALLISTA:
2534 attack_count = 2;
2535 break;
2536 case UNIT_EXPLOSIVE_CART:
2537 attack_count = 0;
2538 break;
2539 default:
2540 attack_count = 1;
2541 }
2542
2543 if( attack_count > 0)
2544 {
2545 attack_info_array += (techLevel-1) * attack_count;
2546 }
2547 else
2548 {
2549 // no attack like explosive cart
2550 attack_info_array = NULL;
2551 }
2552 }
2553 }
2554 //----------- End of function Unit::fix_attack_info -----------//
2555
2556
2557 //----------- Begin of function Unit::return_camp -----------//
2558 //
2559 // Order this unit to return to the camp. For ordering many
2560 // units to return to a camp, UnitArray::return_camp() should
2561 // be called instead.
2562 //
return_camp()2563 int Unit::return_camp()
2564 {
2565 if( !home_camp_firm_recno )
2566 return 0;
2567
2568 err_when( firm_array.is_deleted(home_camp_firm_recno) );
2569
2570 Firm* firmPtr = firm_array[home_camp_firm_recno];
2571
2572 if( firmPtr->region_id != region_id() )
2573 return 0;
2574
2575 err_when( firmPtr->firm_id != FIRM_CAMP );
2576 err_when( firmPtr->nation_recno != nation_recno );
2577
2578 //--------- assign now ---------//
2579
2580 assign(firmPtr->loc_x1, firmPtr->loc_y1);
2581
2582 force_move_flag = 1;
2583
2584 return cur_action != SPRITE_IDLE;
2585 }
2586 //----------- End of function Unit::return_camp -----------//
2587
2588
2589 //----------- Begin of function Unit::unit_power -----------//
2590 //
2591 // Return a power index of the weapon, this is for calculating
2592 // the total combat level of a target.
2593 //
unit_power()2594 int Unit::unit_power()
2595 {
2596 UnitInfo* unitInfo = unit_res[unit_id];
2597
2598 if( unitInfo->unit_class == UNIT_CLASS_WEAPON )
2599 {
2600 return (int) hit_points + (unitInfo->weapon_power + get_weapon_version() - 1) * 15;
2601 }
2602 else
2603 {
2604 return (int) hit_points;
2605 }
2606 }
2607 //----------- End of function Unit::unit_power -----------//
2608
2609
2610 //----------- Begin of function Unit::get_cur_loc -----------//
2611 //
2612 // <short&> xLoc, yLoc - reference vars for returning the
2613 // location of this unit
2614 //
2615 // return : 0 - if this unit is invisible
2616 // 1 - if a location has been returned.
2617 //
get_cur_loc(short & xLoc,short & yLoc)2618 int Unit::get_cur_loc(short& xLoc, short& yLoc)
2619 {
2620 if( is_visible() )
2621 {
2622 xLoc = next_x_loc(); // update location
2623 yLoc = next_y_loc();
2624 }
2625 else if( unit_mode == UNIT_MODE_OVERSEE ||
2626 unit_mode==UNIT_MODE_CONSTRUCT ||
2627 unit_mode == UNIT_MODE_IN_HARBOR )
2628 {
2629 Firm* firmPtr = firm_array[unit_mode_para];
2630
2631 xLoc = firmPtr->center_x;
2632 yLoc = firmPtr->center_y;
2633 }
2634 else if( unit_mode == UNIT_MODE_ON_SHIP )
2635 {
2636 Unit* unitPtr = unit_array[unit_mode_para];
2637
2638 //### begin alex 22/10 ###//
2639 //xLoc = unitPtr->next_x_loc();
2640 //yLoc = unitPtr->next_y_loc();
2641 if(unitPtr->is_visible())
2642 {
2643 xLoc = unitPtr->next_x_loc();
2644 yLoc = unitPtr->next_y_loc();
2645 }
2646 else
2647 {
2648 err_when(unitPtr->unit_mode!=UNIT_MODE_IN_HARBOR);
2649 Firm *firmPtr = firm_array[unitPtr->unit_mode_para];
2650 xLoc = firmPtr->center_x;
2651 yLoc = firmPtr->center_y;
2652 }
2653 //#### end alex 22/10 ####//
2654 }
2655 else
2656 return 0;
2657
2658 return 1;
2659 }
2660 //----------- End of function Unit::get_cur_loc -----------//
2661
2662
2663 //----------- Begin of function Unit::get_cur_loc2 -----------//
2664 //
2665 // <short&> xLoc, yLoc - reference vars for returning the
2666 // location of this unit
2667 //
2668 // return : 0 - if this unit is invisible
2669 // 1 - if a location has been returned.
2670 //
get_cur_loc2(short & xLoc,short & yLoc)2671 int Unit::get_cur_loc2(short& xLoc, short& yLoc)
2672 {
2673 if( is_visible() )
2674 {
2675 xLoc = cur_x_loc();
2676 yLoc = cur_y_loc();
2677 }
2678 else if( unit_mode == UNIT_MODE_OVERSEE ||
2679 unit_mode==UNIT_MODE_CONSTRUCT ||
2680 unit_mode == UNIT_MODE_IN_HARBOR )
2681 {
2682 Firm* firmPtr = firm_array[unit_mode_para];
2683
2684 xLoc = firmPtr->center_x;
2685 yLoc = firmPtr->center_y;
2686 }
2687 else if( unit_mode == UNIT_MODE_ON_SHIP )
2688 {
2689 Unit* unitPtr = unit_array[unit_mode_para];
2690
2691 if( unitPtr->is_visible() )
2692 {
2693 xLoc = unitPtr->cur_x_loc();
2694 yLoc = unitPtr->cur_y_loc();
2695 }
2696 else
2697 {
2698 err_when(unitPtr->unit_mode!=UNIT_MODE_IN_HARBOR);
2699 Firm *firmPtr = firm_array[unitPtr->unit_mode_para];
2700 xLoc = firmPtr->center_x;
2701 yLoc = firmPtr->center_y;
2702 }
2703 }
2704 else
2705 return 0;
2706
2707 return 1;
2708 }
2709 //----------- End of function Unit::get_cur_loc2 -----------//
2710
2711
2712 //----------- Begin of function Unit::is_leader_in_range -----------//
2713 // If the leader is assigned and in range, return leader_unit_recno, otherwise
2714 // return zero for indicating the leader is not in range.
2715 //
is_leader_in_range()2716 short Unit::is_leader_in_range()
2717 {
2718 if( !leader_unit_recno )
2719 return 0;
2720
2721 if( unit_array.is_deleted(leader_unit_recno) )
2722 {
2723 leader_unit_recno = 0;
2724 return 0;
2725 }
2726
2727 Unit* leaderUnit = unit_array[leader_unit_recno];
2728
2729 if( !leaderUnit->is_visible() && leaderUnit->unit_mode == UNIT_MODE_CONSTRUCT )
2730 return 0;
2731
2732 short leaderXLoc, leaderYLoc, xLoc, yLoc;
2733 leaderUnit->get_cur_loc2(leaderXLoc, leaderYLoc);
2734 get_cur_loc2(xLoc, yLoc);
2735
2736 if( leaderXLoc >= 0 && misc.points_distance(xLoc, yLoc, leaderXLoc, leaderYLoc) <= EFFECTIVE_LEADING_DISTANCE )
2737 return leader_unit_recno;
2738
2739 return 0;
2740 }
2741 //----------- End of function Unit::is_leader_in_range -----------//
2742
2743
2744 //----------- Begin of function Unit::add_way_point -----------//
2745 // Add the point to the way_point_array if it is not in the array.
2746 // Otherwise, remove the point from the way_point_array.
2747 //
2748 // <short> x - x coordinate of the point
2749 // <short> y - y coordinate of the point
2750 //
add_way_point(short x,short y)2751 void Unit::add_way_point(short x, short y)
2752 {
2753 if(way_point_count>=100)
2754 return; // too many way point
2755
2756 if(way_point_count>1) // don't allow to remove the 1st node, since the unit is moving there
2757 {
2758 ResultNode *nodePtr = way_point_array + 1;
2759 for(int i=1; i<way_point_count; ++i, nodePtr++)
2760 {
2761 if(nodePtr->node_x == x && nodePtr->node_y == y) // remove this node
2762 {
2763 misc.del_array_rec(way_point_array, way_point_count, sizeof(ResultNode), i+1); // remove 1st node
2764 way_point_count--;
2765 return; // there should be one and only one node with the same value
2766 }
2767 }
2768 }
2769
2770 //-------------- add new node -----------------//
2771 if(way_point_count>=way_point_array_size) // buffer full
2772 {
2773 way_point_array_size += WAY_POINT_ADJUST_SIZE;
2774
2775 if(way_point_count)
2776 way_point_array = (ResultNode*) mem_resize(way_point_array, way_point_array_size*sizeof(ResultNode));
2777 else
2778 way_point_array = (ResultNode*) mem_add(sizeof(ResultNode)*WAY_POINT_ADJUST_SIZE);
2779 }
2780
2781 ResultNode *nodePtr = way_point_array + way_point_count;
2782 nodePtr->node_x = x;
2783 nodePtr->node_y = y;
2784 way_point_count++;
2785
2786 if(way_point_count==1)
2787 move_to(x, y);
2788 }
2789 //----------- End of function Unit::add_way_point -----------//
2790
2791
2792 //----------- Begin of function Unit::reset_way_point_array -----------//
reset_way_point_array()2793 void Unit::reset_way_point_array()
2794 {
2795 //------------------------------------------------------------------------------------//
2796 // There are nly two conditions to reset the way_point_array
2797 // 1) action_mode2!=ACTION_MOVE in Unit::stop()
2798 // 2) dest? != node_? in the first node of way_point_array in calling Unit::move_to()
2799 //------------------------------------------------------------------------------------//
2800 if(way_point_array_size)
2801 {
2802 mem_del(way_point_array);
2803 way_point_array = NULL;
2804 way_point_array_size = 0;
2805 way_point_count = 0;
2806 }
2807 }
2808 //----------- End of function Unit::reset_way_point_array -----------//
2809
2810
2811 //----------- Begin of function Unit::process_way_point -----------//
2812 // move to the next way point and remove this point from the
2813 // way_point_array
2814 //
process_way_point()2815 void Unit::process_way_point()
2816 {
2817 err_when(action_mode!=ACTION_STOP || action_mode2!=ACTION_STOP || cur_action!=SPRITE_IDLE);
2818
2819 int destX, destY;
2820 if(way_point_count>1)
2821 {
2822 ResultNode *nodePtr = way_point_array+1;
2823 destX = nodePtr->node_x;
2824 destY = nodePtr->node_y;
2825 misc.del_array_rec(way_point_array, way_point_count, sizeof(ResultNode), 1); // remove 1st node
2826 way_point_count--;
2827 }
2828 else // only one unprocessed node
2829 {
2830 ResultNode *nodePtr = way_point_array;
2831 destX = nodePtr->node_x;
2832 destY = nodePtr->node_y;
2833 //misc.del_array_rec(way_point_array, way_point_count, sizeof(ResultNode), 1); // remove 1st node
2834 //way_point_count--;
2835 }
2836 move_to(destX, destY);
2837 }
2838 //----------- End of function Unit::process_way_point -----------//
2839
2840
2841 //----------- Begin of function TeamInfo::TeamInfo ---------//
2842
TeamInfo()2843 TeamInfo::TeamInfo()
2844 {
2845 memset( this, 0, sizeof(TeamInfo) );
2846 }
2847 //----------- End of function TeamInfo::TeamInfo ---------//
2848
2849