1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
|
unit SkillUse;
{ This unit should cover the usage of skills for the RPG game. }
{ Actually, it doesn't cover the usage of all skills- most of }
{ them get implemented in other places (combat skills in the }
{ attacker unit, conversation skills in the interact unit, etc). }
{ This unit covers those skills which pretty well need their }
{ own interface/code... repair skills, picking pockets, etc. }
{
GearHead: Arena, a roguelike mecha CRPG
Copyright (C) 2005 Joseph Hewitt
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or (at
your option) any later version.
The full text of the LGPL can be found in license.txt.
This library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
interface
uses gears,locale;
const
Repair_Mental_Strain = 1;
Repair_Max_Tries = 5;
Performance_Range = 9;
Performance_Base_Cash = -50;
TRIGGER_Applause = 'APPLAUSE';
Num_Robot_Skill = 11;
Robot_Skill: Array [1..Num_Robot_Skill] of Byte = (
11,12,15,16,18,
20,23,25,29,31,32
);
Function TotalRepairableDamage( Target: GearPtr; Skill: Integer ): LongInt;
Procedure ApplyRepairPoints( Target: GearPtr; Skill: Integer; var RP: LongInt );
Function UseRepairSkill( GB: GameBoardPtr; PC,Target: GearPtr; Skill: Integer ): LongInt;
Function SeekBestInstrument( PC: GearPtr ): GearPtr;
Function UsePerformance( GB: GameBoardPtr; PC,Instrument: GearPtr ): LongInt;
Function UseRobotics( GB: GameBoardPtr; PC,Ingredients: GearPtr ): GearPtr;
implementation
{$IFDEF SDLMODE}
uses ability,action,damage,gearutil,ghchars,ghholder,ghmodule,ghmovers,ghswag,
ghweapon,movement,interact,rpgdice,texutil,sdlgfx;
{$ELSE}
uses ability,action,damage,gearutil,ghchars,ghholder,ghmodule,ghmovers,ghswag,
ghweapon,movement,interact,rpgdice,texutil;
{$ENDIF}
Function TotalRepairableDamage( Target: GearPtr; Skill: Integer ): LongInt;
{ Search through TARGET, and calculate how much damage it has }
{ that can be repaired using SKILL. }
var
Part: GearPtr;
AD,SD,TCom,SCom,it: LongInt;
T: Integer;
begin
{ Normally damage must be positive I know, but I just had a bug }
{ which resulted in negative damage. This prevented the rest of }
{ the damage to a mek/character from being repaired. So, taking }
{ absolute value should fix all the mess & prevent it from }
{ happening again. }
SD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_StrucDamage ) );
AD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_ArmorDamage ) );
it := 0;
{ If this part is damaged, and if the needed repair skill is }
{ the skill we're looking for, add the damage to the total. }
if RepairSkillNeeded( Target ) = Skill then begin
it := AD + SD;
{ Modify for complexity. }
if not IsMasterGear( Target ) then begin
TCom := ComponentComplexity( Target );
SCom := SubComComplexity( Target );
if SCom > TCom then begin
it := ( it * SCom ) div TCom;
end;
end;
end;
{ Check for status effects. }
for t := 1 to Num_Status_FX do begin
if ( SX_RepSkill[t] = Skill ) and ( NAttValue( Target^.NA , NAG_StatusEffect , T ) <> 0 ) then begin
it := it + SX_RepCost[ T ];
end;
end;
{ Check the sub-components for damage. }
Part := Target^.SubCom;
while Part <> Nil do begin
it := it + TotalRepairableDamage( Part , Skill );
Part := Part^.Next;
end;
{ Check the inv-components for damage. }
Part := Target^.InvCom;
while Part <> Nil do begin
it := it + TotalRepairableDamage( Part , Skill );
Part := Part^.Next;
end;
TotalRepairableDamage := it;
end;
Procedure ApplyRepairPoints( Target: GearPtr; Skill: Integer; var RP: LongInt );
{ Search through TARGET, and restore DPs to parts }
{ that can be repaired using SKILL. }
const
tmp_MAX = 2147483647;
var
Part: GearPtr;
SD,AD,TCom,SCom,ARP,RPNeeded: LongInt;
T: Integer;
tmp: Int64;
begin
{ Only examine TARGET for damage if it's of a type that can be }
{ repaired using SKILL. }
if RepairSkillNeeded( Target ) = Skill then begin
{ Calculate structural damage and armor damage. }
SD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_StrucDamage ) );
if ( SD > 0 ) and ( RP > 0 ) then begin
{ Modify for complexity. }
ARP := RP;
RPNeeded := SD;
if not IsMasterGear( Target ) then begin
TCom := ComponentComplexity( Target );
SCom := SubComComplexity( Target );
if SCom > TCom then begin
tmp := ( Int64(RPNeeded) * Int64(SCom) ) div TCom;
if tmp < 0 then begin
RPNeeded := 0;
end else if tmp_MAX < tmp then begin
RPNeeded := tmp_MAX;
end else begin
RPNeeded := tmp;
end;
tmp := ( Int64(ARP) * Int64(TCom) ) div SCom;
if tmp < 1 then begin
ARP := 1;
end else if tmp_MAX < tmp then begin
ARP := tmp_MAX;
end else begin
ARP := tmp;
end;
end;
end;
SD := SD - ARP;
RP := RP - RPNeeded;
if SD < 0 then SD := 0;
SetNAtt( Target^.NA , NAG_Damage , NAS_StrucDamage , SD );
end;
AD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_ArmorDamage ) );
if ( AD > 0 ) and ( RP > 0 ) then begin
{ Modify for complexity. }
ARP := RP;
RPNeeded := AD;
if not IsMasterGear( Target ) then begin
TCom := ComponentComplexity( Target );
SCom := SubComComplexity( Target );
if SCom > TCom then begin
tmp := ( Int64(RPNeeded) * Int64(SCom) ) div TCom;
if tmp < 0 then begin
RPNeeded := 0;
end else if tmp_MAX < tmp then begin
RPNeeded := tmp_MAX;
end else begin
RPNeeded := tmp;
end;
tmp := ( Int64(ARP) * Int64(TCom) ) div SCom;
if tmp < 1 then begin
ARP := 1;
end else if tmp_MAX < tmp then begin
ARP := tmp_MAX;
end else begin
ARP := tmp;
end;
end;
end;
AD := AD - ARP;
RP := RP - RPNeeded;
if AD < 0 then AD := 0;
SetNAtt( Target^.NA , NAG_Damage , NAS_ArmorDamage , AD );
end;
end;
{ Check for status effects. }
for t := 1 to Num_Status_FX do begin
if ( SX_RepSkill[t] = Skill ) and ( NAttValue( Target^.NA , NAG_StatusEffect , T ) <> 0 ) then begin
if RP >= SX_RepCost[ t ] then begin
RP := RP - SX_RepCost[ t ];
SetNAtt( Target^.NA , NAG_StatusEffect , T , 0 );
end;
end;
end;
{ Check the sub-components for damage. }
Part := Target^.SubCom;
while ( Part <> Nil ) and ( RP > 0 ) do begin
ApplyRepairPoints( Part , Skill , RP );
Part := Part^.Next;
end;
{ Check the inv-components for damage. }
Part := Target^.InvCom;
while ( Part <> Nil ) and ( RP > 0 ) do begin
ApplyRepairPoints( Part , Skill , RP );
Part := Part^.Next;
end;
end;
Function UseRepairSkill( GB: GameBoardPtr; PC,Target: GearPtr; Skill: Integer ): LongInt;
{ The PC wants to use the requested repair SKILL on TARGET. }
{ Roll to see how many DPs will be restored, apply these DPs }
{ to the TARGET, then reduce PC's MPs. }
Function Repair_Skill_Target: Integer;
{ Return a good skill target for repair skills. }
{ This will be decreased as TARGET's scale increases. }
var
RST: Integer;
begin
if Target^.Scale = 0 then begin
if Skill = 16 then begin
RST := 10;
end else begin
RST := 5;
end;
end else if Target^.Scale = 1 then begin
RST := 3;
end else begin
RST := 1;
end;
if Destroyed( Target ) then RST := RST + 10;
Repair_Skill_Target := RST;
end;
var
tries,SkRk,RP,MaxRP,Leftover: LongInt;
DP,DP0,TotalRepaired: LongInt;
begin
{ Depending upon how much damage the target has, the PC can make }
{ several repair attempts. }
PC := LocatePilot( PC );
if PC = Nil then Exit( 0 );
if GB <> Nil then begin
if not MoveLegal( FindRoot( PC ) , NAV_Stop , GB^.ComTime ) then Exit( 0 );
end;
tries := 0;
DP := TotalRepairableDamage( Target , Skill );
DP0 := DP;
MaxRP := ( SkillRank( PC , Skill ) * ( Target^.Scale + 1 ) ) + 1;
SkRk := SkillValue( PC , SKill );
TotalRepaired := 0;
Leftover := 0;
while ( tries < Repair_Max_Tries ) and ( DP > 0 ) and ( CurrentMental( PC ) > 0 ) do begin
RP := RollStep( SkRk ) - Repair_Skill_Target;
if RP > MaxRP then begin
RP := MaxRP;
DoleSkillExperience( PC , Skill , XPA_SK_UseRepair );
end;
if RP > 0 then begin
RP := RP + Leftover;
TotalRepaired := TotalRepaired + RP;
DP := DP - RP;
ApplyRepairPoints( Target , Skill , RP );
Leftover := RP;
DoleExperience( PC , XPA_GoodRepairJob );
end else begin
Leftover := 0;
end;
DoleSkillExperience( PC , Skill , XPA_SK_UseRepair );
AddMentalDown( PC , Repair_Mental_Strain );
Inc( Tries );
end;
{ Advance time by the required amount. }
if HasTalent( PC , NAS_CombatMedic ) and (( Skill = 20 ) or ( Skill = 16 )) and ( not IsSafeArea( GB ) ) then begin
WaitAMinute( GB , PC , ( ReactionTime( PC ) * Tries div 3 ) + 1 );
AddStaminaDown( PC , Tries div 2 );
end else begin
WaitAMinute( GB , PC , ReactionTime( PC ) * Tries );
end;
UseRepairSkill := DP0 - TotalRepairableDamage( Target, Skill );
end;
Function SeekBestInstrument( PC: GearPtr ): GearPtr;
{ Check the PC's inventory for the best instrument to use. }
var
Item,Best: GearPtr;
begin
Best := Nil;
Item := PC^.InvCom;
while Item <> Nil do begin
if ( Item^.G = GG_Usable ) and ( Item^.S = GS_Instrument ) and NotDestroyed( Item ) then begin
if Best = Nil then Best := Item
else if Item^.Stat[ STAT_UseBonus ] > Best^.Stat[ STAT_UseBonus ] then Best := Item;
end;
Item := Item^.Next;
end;
SeekBestInstrument := Best;
end;
Function UsePerformance( GB: GameBoardPtr; PC,Instrument: GearPtr ): LongInt;
{ The PC is about to use a performance skill. }
{ As a result, the following things may happen: }
{ - Earn money through tips. }
{ - Set "APPLAUSE" triggers for positive reactions. }
{ - Modify Morale up or down. }
{ - Earn skill experience for performance. }
{ - Lose Mental and Stamina from the act of playing. }
{ Return -1 for a bad performance, 0 for a mediocre performance, }
{ and a positive number if the PC made any tips. }
var
Perf: Integer; { Modified performance ranking. }
SkRoll,Target: Integer; { Skill roll target }
N: Integer; { Number of successes }
Cash: LongInt;
M: GearPtr;
begin
{ Determine the performance skill, and modify for instrument. }
Perf := SkillValue( PC , NAS_Performance ) + Instrument^.Stat[ STAT_UseBonus ];
{ Reduce stamina and mental now. }
{ Performing is both mentally and physically exhausting. }
if Random( 2 ) = 1 then begin
AddStaminaDown( PC , 1 );
end else begin
AddMentalDown( PC , 1 );
end;
if ( CurrentMental( PC ) > 0 ) and ( CurrentStamina( PC ) > 0 ) then begin
{ Give out some skill experience, as long as the PC is }
{ not yet exhausted. }
DoleSkillExperience( PC , NAS_Performance , 1 );
end;
{ Check through the audience. For the purpose of this game, }
{ the audience counts as every nonhostile NPC within [UseRange] tiles. }
N := 0;
Cash := 0;
{ Modify N for a low reputation. }
if NAttValue( PC^.NA , NAG_CharDescription , NAS_Renowned ) < 1 then Dec( N );
M := GB^.Meks;
while M <> Nil do begin
{ If M is a character, not the PC, and is active, }
{ and is not hostile towards the PC, and is in range, }
{ check its reaction to the performance. }
if ( M^.G = GG_Character ) and ( M <> PC ) and ( Range( GB , M , PC ) <= Instrument^.Stat[ STAT_UseRange ] ) and GearActive( M ) and ( not AreEnemies( GB , M , PC ) ) then begin
{ Calculate the target number. }
Target := ( CStat( M , STAT_Ego ) * 3 div 2 ) + NAttValue( M^.NA , NAG_Personal , NAS_PerformancePenalty );
if Target < 10 then Target := 10;
SkRoll := RollStep( Perf );
if SkRoll > Target then begin
Inc( N );
if Random( 2 ) = 1 then AddNAtt( M^.NA , NAG_Personal , NAS_PerformancePenalty , 1 );
if SkRoll > ( Target * 2 ) then Inc( N );
if ( CurrentMental( PC ) > 0 ) and ( CurrentStamina( PC ) > 0 ) then begin
DoleSkillExperience( PC , NAS_Performance , 1 );
DoleExperience( PC , 1 );
end;
SetTrigger( GB , TRIGGER_Applause );
end else if ( SkRoll + PersonalityCompatability( PC , M ) - 5 ) < 0 then begin
Dec( N );
end;
end;
M := M^.Next;
end;
{ If the PC earned any money from busking, add that here. }
if ( N >= 2 ) then begin
DoleExperience( PC , N div 2 );
Dec( N );
if ( CurrentMental( PC ) > 0 ) and ( CurrentStamina( PC ) > 0 ) then begin
if N > NAttValue( PC^.NA , NAG_Skill , NAS_Performance ) then N := NAttValue( PC^.NA , NAG_Skill , NAS_Performance );
Cash := SkillAdvCost( Nil , N ) div 10;
AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , Cash );
end;
end else if ( N < 0 ) then begin
{ The PC did pretty badly. }
AddMoraleDmg( PC , Rollstep( 1 ) );
Cash := -1;
end;
UsePerformance := Cash;
end;
Function RandomRobotName: String;
{ Generate random St*r-W*rs sounding robot name. }
const
NumLetter = 30;
Letters: Array [1..NumLetter] of char = (
'A','B','C','D','E', 'F','G','H','I','J',
'K','L','M','N','O', 'P','Q','R','S','T',
'U','V','W','X','Y', 'Z','C','P','D','R'
);
Function AlphaNum: String;
{ Generate a random sequence of letters and numbers. }
var
msg: String;
begin
msg := Letters[ Random( NumLetter ) + 1 ];
if Random( 2 ) = 1 then msg := msg + Letters[ Random( NumLetter ) + 1 ];
if Random( 2 ) = 1 then msg := msg + BStr( Random( 10 ) )
else msg := BStr( Random( 10 ) ) + msg;
if Random( 10 ) = 1 then msg := msg + BStr( Random( 10 ) )
else if Random( 9 ) = 1 then msg := BStr( Random( 10 ) ) + msg
else if Random( 8 ) = 1 then msg := msg + Letters[ Random( NumLetter ) + 1 ]
else if Random( 8 ) = 1 then msg := Letters[ Random( NumLetter ) + 1 ] + msg;
AlphaNum := msg;
end;
Function JustNum: String;
{ Return a random sequence of numbers. }
begin
JustNum := BStr( Random( 499 ) + Random( 489 ) + 10 );
end;
var
name: String;
begin
name := AlphaNum;
repeat
if Random( 2 ) = 1 then begin
name := name + '-' + AlphaNum;
end else begin
name := name + '-' + JustNum;
end;
until ( Length( name ) > 10 ) or ( Random( 2 ) = 1 );
RandomRobotName := name;
end;
Function UseRobotics( GB: GameBoardPtr; PC,Ingredients: GearPtr ): GearPtr;
{ Given the above list of ingredients, the PC will try to construct a robot. }
{ Here are the rules: }
{ - Ingredients provide build points. }
{ - BODY of the robot to be determined by the total amount of build points. }
{ - PERCEPTION, CRAFT, KNOWLEDGE determined by PC's Robotics skill roll. }
{ - SPEED, REFLEXES determined by skill roll, reduced by BODY. }
{ - CHARM is 1, unless robot gains self-awareness. }
{ - All robots get a body. }
{ - If self-aware, give the robot a humanoid body }
{ - Roll for movement type: Wheels, Tracks, Legs-4, Legs-2, Hover, Flight }
{ - Add movement modules: STORAGE, LEGS, or WINGS }
{ - Add combat modules: ARM, TURRET, HEAD, TAIL }
{ Wow, this is really complicated. I might have second thoughts about }
{ adding a GENE BLENDER for the BioTech skill later on... }
{ This function returns the robot, or NIL if construction failed. }
{ The calling procedure should place the robot on the map or dispose of it. }
const
BP_MAX = 2147483647;
BP_MIN = -2147483648;
Stat_MAX = 32767;
var
Robot,Part,Part2: GearPtr;
BP: LongInt;
BP_tmp: Int64;
Bulk,SkRk,T,BaseSkill,Sensor,Electronic,Armor,Skill: Integer;
Viable,Good: Boolean;
Procedure InstallLimb( N,Size: Integer );
{ Install a limb into the robot. }
var
M,H: GearPtr;
begin
M := AddGear( Robot^.SubCom , Robot );
if Size < 1 then Size := 1;
M^.G := GG_Module;
M^.S := N;
M^.V := Size;
InitGear( M );
M^.Stat[ STAT_Armor ] := Size;
if N = GS_Arm then begin
if RollStep( SkRk ) > 5 then begin
H := AddGear( M^.SubCom , M );
H^.G := GG_Holder;
H^.S := GS_Hand;
InitGear( H );
end;
end else if N = GS_Wing then begin
H := AddGear( M^.SubCom , M );
H^.G := GG_MoveSys;
H^.S := GS_FlightJets;
H^.V := M^.V * 2;
InitGear( H );
if RollStep( SkRk ) > 16 then begin
H := AddGear( M^.SubCom , M );
H^.G := GG_Holder;
H^.S := GS_Mount;
InitGear( H );
SetSAtt( H^.SA , 'NAME <' + GearName( M ) + ' ' + GearName( H ) + '>' );
end;
end else if N = GS_Storage then begin
H := AddGear( M^.SubCom , M );
H^.G := GG_MoveSys;
H^.S := GS_Tracks;
H^.V := M^.V;
InitGear( H );
end else if N <> GS_Body then begin
if RollStep( SkRk ) >= 12 then begin
H := AddGear( M^.SubCom , M );
H^.G := GG_Holder;
H^.S := GS_Mount;
InitGear( H );
SetSAtt( H^.SA , 'NAME <' + GearName( M ) + ' ' + GearName( H ) + '>' );
end;
end;
end;
function StatImprovementCost( CurrentVal: Integer ): Integer;
{ Return the number of build points needed to improve a stat. }
begin
if CurrentVal < 10 then StatImprovementCost := 1
else StatImprovementCost := CurrentVal - 8;
end;
function RobotStats( RobotC: GearPtr; Pts,MaxStat: Integer): Integer;
{ Randomly allocate PTS points to all of the robot's }
{ stats. }
var
Stat, Tries: Integer; { A loop counter. }
STemp: Array [1..NumGearStats] of Integer;
begin
for Stat := 1 to NumGearStats do begin
STemp[Stat] := 0;
end;
{ Keep processing until we run out of stat points to allocate. }
Tries := 0;
while ( Pts > 0 ) and ( Tries < 100 ) do begin
Stat := Random( 7 ) + 1;
{ If the stat selected is under the max value, }
{ improve it. If it is at or above the max value, }
{ there's a one in three chance of improving it. }
if ( STemp[Stat] + RobotC^.Stat[ Stat ] ) < MaxStat then begin
Pts := Pts - StatImprovementCost( STemp[Stat] );
Inc( STemp[Stat] );
end else begin
Inc( Tries );
end;
end;
{ Add the STemp values to the stat baseline. }
for Stat := 1 to NumGearStats do RobotC^.Stat[Stat] := RobotC^.Stat[Stat] + STemp[Stat];
{ Return the number of unspent points. }
RobotStats := Pts;
end;
begin
{ PC must have some energy to do this. }
if CurrentMental( PC ) < 1 then begin
DisposeGear( Ingredients );
Exit( Nil );
end;
{ Start with allocating the robot's base gear. }
Robot := NewGear( Nil );
Robot^.G := GG_Character;
InitGear( Robot );
SetNAtt( Robot^.NA , NAG_GearOps , NAS_Material , NAV_Metal );
SetSAtt( Robot^.SA , 'TYPE <ROBOT>' );
SetSAtt( Robot^.SA , 'JOB <ROBOT>' );
SetSAtt( Robot^.SA , 'NAME <' + RandomRobotName + '>' );
SetNAtt( Robot^.NA , NAG_CharDescription , NAS_DAge , -19 );
SetSAtt( Robot^.SA , 'ROGUECHAR <R>' );
SetNAtt( Robot^.NA, NAG_CharDescription, NAS_Gender, NAV_Undefined );
SetNAtt( Robot^.NA, NAG_CharDescription, NAS_Sentience, NAV_IsMonster );
{$IFDEF SDLMODE}
SetSAtt( Robot^.SA, 'SDL_COLORS <' + RandomColorString(CS_Clothing) + ' ' + RandomColorString(CS_PrimaryMecha) + ' ' + RandomColorString(CS_Detailing) + '>' );
{$ELSE}
SetSAtt( Robot^.SA , 'SDL_COLORS <80 80 85 170 155 230 6 42 120>' );
{$ENDIF}
{ Determine the PC's ROBOTICS skill. }
SkRk := TeamSkill( GB , NAV_DefPlayerTeam , 38 );
PC := LocatePilot( PC );
BaseSkill := NAttValue( PC^.NA , NAG_Skill , 38 );
{ Add the stamina decrease here. }
AddMentalDown( PC , 10 );
{ Give some experience. }
DoleSkillExperience( PC , 38 , NumSiblingGears( Ingredients ) );
DoleExperience( PC , 5 );
{ Count the BPs provided by the ingredient list. }
BP := 0;
Part := Ingredients;
while Part <> Nil do begin
if Part^.G = GG_RepairFuel then begin
BP := BP + Part^.V;
end else begin
BP_tmp := BP;
BP_tmp := BP_tmp + GearValue( Part ) div 10;
if BP_tmp < BP_MIN then begin
BP_tmp := BP_MIN;
end else if BP_MAX < BP_tmp then begin
BP_tmp := BP_MAX;
end;
BP := BP_tmp;
end;
Part := Part^.Next;
end;
{ Roll the stats. }
BP := RobotStats( Robot, BP div 20 + RollStep( SkRk ), SkRk - 2 );
{ Make sure nothing has gone below 0. }
{ If all the stats are above 10, maybe make robot self-aware. }
Viable := True;
Good := True;
for t := 1 to 7 do begin
if Robot^.Stat[ t ] > ( BaseSkill * 2 + 1 ) then Robot^.Stat[ t ] := ( BaseSkill * 2 + 1 );
if Robot^.Stat[ t ] < 1 then begin
if CurrentMental( PC ) >= Abs( Robot^.Stat[ t ] * 2 ) then begin
AddMentalDown( PC , Abs( Robot^.Stat[ t ] ) * 2 );
Robot^.Stat[ t ] := 1 + Random( 3 );
end else begin
Viable := False;
end;
end;
if Robot^.Stat[ t ] < 10 then begin
Good := False;
end;
end;
{ Self-aware robots may have CIDs. Other robots may not. }
if Good then begin
Robot^.Stat[ STAT_Charm ] := RollStep( SkRk ) - 10;
if Robot^.Stat[ STAT_Charm ] > 10 then begin
{ This robot has become self-aware!!! }
{ Give it a CID, a gender, and it likes the PC. }
SetNAtt( Robot^.NA , NAG_Personal , NAS_CID , NewCID( GB , FindRoot( GB^.Scene ) ) );
SetNAtt( Robot^.NA , NAG_CharDescription , NAS_Gender , Random( 3 ) );
SetNAtt( Robot^.NA, NAG_CharDescription, NAS_Sentience, NAV_IsCharacter );
AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( Robot^.NA , NAG_Personal , NAS_CID ) , 50 );
{ Give the PC some extra XP for a job well done. }
DoleSkillExperience( PC , 38 , 50 );
DoleExperience( PC , 100 );
end else begin
Robot^.Stat[ STAT_Charm ] := 1;
Good := False;
end;
end else begin
Robot^.Stat[ STAT_Charm ] := 1;
end;
{ Finally, if the robot is viable, give it a body. }
{ Otherwise it gets nothing. }
if Viable then begin
{ First, our robot needs a body. Self-aware robots get humanoid bodies. }
{ Other robots get random bodies. }
if Good then begin
ExpandCharacter( Robot );
for t := 1 to 5 do SetNAtt( Robot^.NA , NAG_Skill , T , SkRk div 2 );
end else begin
InstallLimb( GS_Body , MasterSize( Robot ) );
if RollStep( SkRk ) > 20 then begin
InstallLimb( GS_Wing , MasterSize( Robot ) );
InstallLimb( GS_Wing , MasterSize( Robot ) );
end else if RollStep( SkRk ) > 10 then begin
if Random( 5 ) = 1 then begin
for t := 1 to 3 do InstallLimb( GS_Leg , MasterSize( Robot ) - 1 );
end else if Random( 3 ) = 1 then begin
for t := 1 to 2 do InstallLimb( GS_Leg , MasterSize( Robot ) );
end else begin
for t := 1 to 4 do InstallLimb( GS_Leg , MasterSize( Robot ) - 1 );
end;
end else begin
for t := 1 to 2 do InstallLimb( GS_Storage , MasterSize( Robot ) );
end;
SetSAtt( Robot^.SA , 'SDL_SPRITE <monster_drone.png>' );
end;
{ Give our robot some skills. }
for t := 6 to 10 do SetNAtt( Robot^.NA , NAG_Skill , T , SkRk div 3 + 1 );
SetNAtt( Robot^.NA , NAG_Skill , NAS_WeightLifting , 10 );
SetNAtt( Robot^.NA , NAG_Skill , 26 , 5 );
SetNAtt( Robot^.NA , NAG_Skill , 30 , 5 );
{ The base skill level determines how many perks this robot }
{ will get. Start by installing weapons and other ingredients. }
Part := Ingredients;
Sensor := 0;
Electronic := 0;
Armor := 0;
while Part <> Nil do begin
Part2 := Part^.Next;
if ( Part^.G = GG_Weapon ) and ( RollStep( SkRk ) > Part^.V ) and ( BaseSkill > 0 ) then begin
DelinkGear( Ingredients , Part );
InsertSubCom( SelectRandomGear( Robot^.SubCom ) , Part );
Dec( BaseSkill );
end else if ( Part^.G = GG_Sensor ) and ( Part^.V > Sensor ) then begin
Sensor := Part^.V;
end else if ( Part^.G = GG_Electronics ) and ( Part^.V > Electronic ) then begin
Electronic := Part^.V;
end else if ( Part^.G = GG_ExArmor ) and ( Part^.V > Armor ) then begin
Armor := Part^.V;
end;
Part := Part2;
end;
Robot^.Stat[ STAT_Perception ] := Robot^.Stat[ STAT_Perception ] + ( Sensor div 2 );
Robot^.Stat[ STAT_Knowledge ] := Robot^.Stat[ STAT_Knowledge ] + ( Electronic div 2 );
AddNAtt( Robot^.NA , NAG_Skill , 13 , Armor );
{ Each of the following perks costs two skill points, so }
{ halve the BaseSkill value. }
BaseSkill := BaseSkill div 2;
for t := 1 to BaseSkill do begin
if Random( 3 ) = 1 then begin
{ Add a module. }
if Random( 2 ) = 1 then InstallLimb( GS_Arm , MasterSize( Robot ) )
else if Random( 5 ) = 2 then InstallLimb( GS_Head , MasterSize( Robot ) )
else if Random( 5 ) = 2 then InstallLimb( GS_Tail , MasterSize( Robot ) )
else InstallLimb( GS_Turret , MasterSize( Robot ) );
end else if Random( 5 ) = 1 then begin
{ Add a specialist skill, maybe. }
Skill := Robot_Skill[ Random( Num_Robot_Skill ) + 1 ];
AddNAtt( Robot^.NA , NAG_Skill , Skill , Random( BaseSkill ) + 1 );
end else begin
{ Improve a stat. }
Inc( Robot^.Stat[ Random( 7 ) + 1 ] );
end;
end;
{ Give some XP for a successful robot. }
DoleSkillExperience( PC , 38 , 10 );
DoleExperience( PC , 50 );
end else begin
{ The construction attempt has failed. }
{DoleSkillExperience( PC, 38, 10 );}
DisposeGear( Robot );
end;
{ Advance time by the required amount. }
WaitAMinute( GB , PC , ReactionTime( PC ) * 10 );
DisposeGear( Ingredients );
UseRobotics := Robot;
end;
end.
|