1 #include "twcgamecontroller.h"
2 #include "twcplayerclient.h"
4 #include <wvstrutils.h>
6 TWCShip::TWCShip(UniConf _cfg
, TWCGameController
*_game
)
7 : TWCObject(_cfg
, _game
), warping_to(NULL
)
12 TWCSector
*TWCShip::sector()
14 return game
->get_sector(TWCSectorCoord(cfg
["location"].getme()));
18 TWCPlayerClient
*TWCShip::captain()
20 return game
->curplayers
[cfg
["captain"].getmeint()];
24 TWCPlayer
*TWCShip::owner()
26 return game
->players
[cfg
["owner"].getmeint()];
30 TWCShieldClass
*TWCShip::shieldclass()
32 // Eventually we'll support more than one shield.
34 UniConf::Iter
i(cfg
["shields"]);
35 for (i
.rewind(); i
.next(); )
38 shield
= i().key().printable().num();
42 return game
->shieldclasses
[shield
];
46 TWCShipClass
*TWCShip::shipclass()
48 return game
->shipclasses
[cfg
["class"].getmeint()];
52 TWCWeaponClass
*TWCShip::weaponclass()
54 // Eventually we'll support more than one weapon.
56 UniConf::Iter
i(cfg
["weapons"]);
57 for (i
.rewind(); i
.next(); )
60 weapon
= i().key().printable().num();
64 return game
->weaponclasses
[weapon
];
68 bool TWCShip::weapon_charged()
70 return cfg
["weapon charged"].getmeint();
74 int TWCShip::free_holds()
76 int res
= total_holds();
78 UniConf::Iter
i(cfg
["commodities"]);
79 for (i
.rewind(); i
.next(); )
81 if (isint(i().key().printable()))
82 res
-= i().getmeint();
89 bool TWCShip::install_weapon(TWCWeaponClass
*weapon
)
91 if (!weapon
|| weaponclass())
94 cfg
["weapons"][weapon
->id
].setmeint(1);
99 bool TWCShip::uninstall_weapon(TWCWeaponClass
*weapon
)
104 cfg
["weapons"][weapon
->id
].remove();
109 bool TWCShip::install_shield(TWCShieldClass
*shield
)
111 if (!shield
|| shieldclass())
114 cfg
["shields"][shield
->id
].setmeint(1);
115 cfg
["shield strength"].setmeint(shield
->strength());
120 bool TWCShip::uninstall_shield(TWCShieldClass
*shield
)
125 cfg
["shields"][shield
->id
].remove();
130 bool TWCShip::load(int type
, int num
)
132 game
->log("Loading %s of type %s.\n", num
, type
);
133 if (free_holds() < num
)
136 incr_uniconf(cfg
["commodities"][type
], num
);
141 bool TWCShip::unload(int type
, int num
)
143 if (cfg
["commodities"][type
].getmeint() < num
)
146 decr_uniconf(cfg
["commodities"][type
], num
);
151 void TWCShip::inventory(TWCShipInventoryList
&inv
)
153 TWCShipInventory
*item
;
154 UniConf::Iter
i(cfg
["commodities"]);
155 for (i
.rewind(); i
.next(); )
159 item
= new TWCShipInventory
;
160 item
->commodity
= game
->commodities
[i().key().printable().num()];
161 item
->number
= i().getmeint();
162 inv
.append(item
, true);
167 bool TWCShip::beam_cargo_aboard(TWCShip
*from_ship
, int type
, int num
)
169 if (from_ship
->sector() != sector())
171 if (!from_ship
->unload(type
, num
))
173 if (!load(type
, num
))
175 from_ship
->load(type
, num
);
182 TWCShip
*TWCShip::towing()
184 return game
->ships
[cfg
["towing"].getmeint()];
188 TWCShip
*TWCShip::towed_by()
190 return game
->ships
[cfg
["towed by"].getmeint()];
194 bool TWCShip::tow(TWCShip
*target
)
201 TWCShip
*oldtowing
= towing();
202 cfg
["towing"].setmeint(0);
203 oldtowing
->tow_attempt(NULL
);
204 sector()->announce(TractorBeamDropped
, id
, oldtowing
->id
,
205 WvString("%s deactivates the tractor beam on %s.\n",
206 name(), oldtowing
->name()), id
);
210 if (target
->tow_attempt(this))
212 cfg
["towing"].setmeint(target
->id
);
213 sector()->announce(TractorBeamEngaged
, id
, target
->id
,
214 WvString("%s locks %s in a tractor beam.\n",
215 name(), target
->name()), id
);
222 bool TWCShip::tow_attempt(TWCShip
*by
)
226 cfg
["being towed"].setmeint(0);
233 cfg
["being towed"].setmeint(by
->id
);
234 cfg
["towing"].setmeint(0); // just to be sure
239 TWCObjectIf
*TWCShip::create_if(TWCHumanPlayerClient
&plyrcli
)
241 return new TWCShipIf(*this, plyrcli
);
245 void TWCShip::set_captain(TWCPlayerClient
*plyrcli
)
248 cfg
["captain"].setmeint(plyrcli
->player
.id
);
250 cfg
["captain"].setme("");
254 void TWCShip::raise_shields()
256 cfg
["shields up"].setmeint(1);
257 sector()->announce(ShieldsRaised
, id
, 0,
258 WvString("%s raises its shields.\n", name()), id
);
262 void TWCShip::lower_shields()
264 cfg
["shields up"].setmeint(0);
265 sector()->announce(ShieldsDropped
, id
, 0,
266 WvString("%s lowers its shields.\n", name()), id
);
270 TWCDockResults
TWCShip::dock()
272 TWCPort
*port
= sector()->port();
275 TWCDockResults res
= port
->docking(this);
276 if (res
== DockSuccess
)
277 game
->curplayers
[captain()->player
.id
]->set_location(port
);
285 void TWCShip::charge_weapon(int)
287 if (!weapon_charged())
289 cfg
["weapon charged"].setmeint(1);
291 captain()->msg("Weapon charged.\n");
296 WvString
TWCShip::viable_target(WvStringParm shipnum
)
298 TWCShip
*ship
= game
->ships
[shipnum
.num()];
300 return WvString("No such ship!\n");
303 return WvString("You can't target your own ship!\n");
305 if (ship
->sector() != sector())
306 return WvString("This ship isn't here!\n");
308 return WvString::null
;
312 TWCAttackResults
TWCShip::shoot(TWCShip
*target
, int power
)
317 if (!weapon_charged())
320 // automatically raise shields
321 if (!target
->are_shields_up())
322 target
->raise_shields();
324 game
->log("%s is attacking %s at %s%% power.\n", name(), target
->name(),
326 TWCAttackResults res
= Damaged
;
328 int attack_roll
= rand() % 100;
329 game
->log("Attacker rolls %s\n", attack_roll
);
330 if (attack_roll
< weaponclass()->accuracy())
333 int mean_damage
= weaponclass()->damage();
334 int damage
= (int)box_muller(mean_damage
, mean_damage
* 0.1) * power
339 bool still_alive
= target
->damage_ship(damage
);
341 game
->log("Attacker hit for %s damage (mean %s).\n", damage
,
344 sector()->announce(ShipAttackHit
, id
, target
->id
,
345 WvString("%s blasts %s with its %s.\n",
346 name(), target
->name(),
347 weaponclass()->name()),
348 captain()->player
.id
);
356 game
->log("attacker missed.\n");
357 sector()->announce(ShipAttackMissed
, id
, target
->id
,
358 WvString("%s misses %s with its %s.\n",
359 name(), target
->name(),
360 weaponclass()->name()),
361 captain()->player
.id
);
362 captain()->msg("We missed, captain.\n");
365 cfg
["weapon charged"].setmeint(0);
366 game
->events
.add_event(TWCEventCallback(this, &TWCShip::charge_weapon
),
367 0, weaponclass()->recharge_time(), true);
373 bool TWCShip::damage_ship(int dmg
)
375 decr_uniconf(cfg
["shield strength"], dmg
);
376 if (shield_strength() < 0)
378 decr_uniconf(cfg
["hull strength"], shield_strength() * -1);
379 cfg
["shield strength"].setmeint(0);
381 if (hull_strength() <= 0)
383 sector()->announce(ShipDestroyed
, id
, 0, WvString("The %s explodes!\n",
385 captain() ? captain()->player
.id
: 0);
388 captain()->msg("Can't... hold it... together...\n");
392 game
->remove_ship(this);
397 if (shield_strength())
398 captain()->msg(WvString("Shields at %s percent, captain.\n",
399 shield_strength() * 100 /
400 shieldclass()->strength()));
402 captain()->msg(WvString("Hull at %s percent, captain.\n",
403 hull_strength() * 100 /
404 shipclass()->hull_strength()));
410 void TWCShip::do_warp(int)
412 sector()->ship_warp_out(this);
413 cfg
["location"].setme(warping_to
->coord
);
415 sector()->ship_warp_in(this);
420 void TWCShip::start_warp(TWCEventCallback _post_warp
, TWCSector
*dest
)
422 post_warp
= _post_warp
;
424 game
->events
.add_event(TWCEventCallback(this, &TWCShip::do_warp
), 0, 2,
429 void TWCShip::set_by_player(TWCPlayerClient
*plyrcli
)
431 set_captain(plyrcli
);
436 bool TWCShip::check_claim_ownership(TWCPlayer
*player
)
438 TWCPlayer
*oldowner
= owner();
439 if (!oldowner
|| oldowner
!= player
)
441 oldowner
->remove_ship(this);
442 cfg
["owner"].setmeint(player
->id
);
443 player
->add_ship(this);
450 TWCShipIf::TWCShipIf(TWCShip
&_ship
, TWCHumanPlayerClient
&_plyrcli
)
451 : TWCObjectIf(_ship
, _plyrcli
), ship(_ship
), autopilot(false),
452 tactical(false), transporter(false), final_dest(NULL
)
458 TWCShipIf::~TWCShipIf()
465 void TWCShipIf::print_menu()
471 plyrcli
.print(ship
.game
->get_screen("tactical"));
472 else if (transporter
)
473 plyrcli
.print(ship
.game
->get_screen("transporter"));
475 plyrcli
.print(ship
.game
->get_screen("ship"));
479 void TWCShipIf::menu_input()
483 warping_menu_input();
488 tactical_menu_input();
491 else if (transporter
)
493 transporter_menu_input();
497 char *line
= plyrcli
.getline(0);
507 if (ship
.are_shields_up())
509 plyrcli
.print("You lower your ship's shields.\n");
510 ship
.lower_shields();
514 plyrcli
.print("You raise your ship's shields.\n");
515 ship
.raise_shields();
522 plyrcli
.print("To which sector (x y z)? ");
523 line
= plyrcli
.continue_getline();
526 ship_move(TWCSectorCoord(trim_string(line
)));
533 if (!plyrcli
.player
.current_turns())
535 plyrcli
.print("You don't have enough turns left.\n");
539 TWCDockResults res
= ship
.dock();
540 if (res
== DockSuccess
)
542 ship
.docked_with
= ship
.sector()->port();
543 plyrcli
.print("Docked with %s.\n", ship
.docked_with
->name());
544 plyrcli
.player
.deduct_turns(1);
546 else if (res
== NoPort
)
547 plyrcli
.print("\e[1;31mMove along... nothing to dock with "
550 plyrcli
.print("Only one ship can be docked with trading ports at a "
576 plyrcli
.print("\n\n");
577 plyrcli
.print("\e[0;32mStats\e[0;37m\n");
578 plyrcli
.print("\e[1;33m-=-=-\e[0;37m\n");
579 plyrcli
.print("\e[0;32mMoves: \e[1;33m%s\e[0;32m/\e[1;33m%s\n"
580 "\e[0;32mExperience: \e[1;33m%s\n"
581 "\e[0;32mCash: \e[1;33m%s\n"
582 "\e[0;32mCurrent Ship: \e[1;33m%s \e[0;32m(\e[0;33m%s"
583 "\e[0;32m)\e[0;37m\n"
584 "\e[0;32mShield: \e[1;33m%s %s\e[0;32m/\e[1;33m%s\n"
585 "\e[0;32mWeapon: \e[1;33m%s\e[0;37m\n\n"
586 "Press Enter to continue...\n",
587 plyrcli
.player
.current_turns(), plyrcli
.player
.max_turns(),
588 plyrcli
.player
.experience(),
589 ship
.game
->price_string(plyrcli
.player
.cash()),
590 ship
.name(), ship
.shipclass()->name(),
591 ship
.shieldclass() ? ship
.shieldclass()->name() : WvString("none"),
592 ship
.shieldclass() ? ship
.shield_strength() : WvString(0),
593 ship
.shieldclass() ? ship
.shieldclass()->strength() : WvString(0),
594 ship
.weaponclass() ? ship
.weaponclass()->name() : WvString("none")
596 plyrcli
.continue_getline();
609 TWCShip
*target
= plyrcli
.acquire_target("Tow which ship",
610 "You cannot scan a docked "
613 plyrcli
.display_inventory(target
);
620 TWCShip
*towing
= ship
.towing();
623 plyrcli
.print("Stop towing the %s? ", towing
->name());
624 line
= plyrcli
.continue_getline();
627 line
= trim_string(line
);
628 if (line
[0] == 'y' || line
[0] == 'Y')
630 plyrcli
.print("Tractor beam deactivated.\n");
636 TWCShip
*target
= plyrcli
.acquire_target("Tow which ship",
637 "You cannot tow a docked "
642 if (target
->captain())
644 plyrcli
.print("You can only tow ships without an active "
649 if (ship
.tow(target
))
651 int move_cost
= ship
.shipclass()->move_cost() +
652 target
->shipclass()->move_cost();
654 plyrcli
.print("You are now towing the %s. Your new movement cost "
655 "is %s turns per sector.\n", target
->name(),
659 plyrcli
.print("Couldn't tow the %s.\n", target
->name());
666 ship
.set_captain(NULL
);
667 plyrcli
.set_location(ship
.game
->nowhere());
671 plyrcli
.print(ship
.game
->screen("ship"));
680 WvString
TWCShipIf::prompt()
685 return WvString("%s< %sShip %s> %s%s%s/%s%s%s%s", ANSI_YELLOW
,
686 ANSI_GREEN
, ANSI_YELLOW
, ANSI_GREEN
,
687 plyrcli
.player
.current_turns(), ANSI_YELLOW
, ANSI_GREEN
,
688 plyrcli
.player
.max_turns(), ANSI_RED
,
689 ship
.cfg
["shields up"].getmeint() ? "" : " *shields down*");
693 void TWCShipIf::ship_move(const TWCSectorCoord dest_sector
)
697 plyrcli
.print("Invalid sector.\n");
701 TWCSectorCoord cur_sector
= ship
.sector()->coord
;
702 if (cur_sector
== dest_sector
)
704 plyrcli
.print("You're already in that sector.\n");
708 // check to see if the destination actually exists
709 TWCSectorCoord next_hop
= ship
.game
->find_route(cur_sector
, dest_sector
);
710 if (next_hop
== cur_sector
)
712 plyrcli
.print("Sector %s does not exist.\n", dest_sector
);
716 // if no match was found (above) then we need to
717 // find a path to the destination
718 if (next_hop
!= dest_sector
)
720 TWCSectorCoord chain
= next_hop
;
722 plyrcli
.print("Sector %s not adjacent; calculating route.\n[%s] ",
723 dest_sector
, next_hop
);
725 while (chain
!= dest_sector
)
727 chain
= ship
.game
->find_route(chain
, dest_sector
);
728 plyrcli
.print("[%s] ", chain
);
732 plyrcli
.print("\nEngage [yna]? ");
733 line
= plyrcli
.continue_getline();
737 if (line
[0] == 'a' || line
[0] == 'A')
739 else if (line
[0] != 'y' && line
[0] != 'Y')
743 final_dest
= new TWCSectorCoord(dest_sector
);
744 if (checkmoveturns())
745 ship
.start_warp(TWCEventCallback(this, &TWCShipIf::post_warp
),
746 ship
.game
->get_sector(next_hop
));
751 void TWCShipIf::warping_menu_input()
753 char *line
= plyrcli
.getline(0);
754 if (!line
|| ship
.warping())
767 TWCSectorCoord next_hop
= ship
.game
->find_route(ship
.sector()->coord
,
769 ship
.start_warp(TWCEventCallback(this, &TWCShipIf::post_warp
),
770 ship
.game
->get_sector(next_hop
));
780 plyrcli
.print("Continue with trip? [yna]");
785 void TWCShipIf::post_warp(int)
788 if (ship
.sector()->coord
!= *final_dest
)
790 TWCSectorCoord next_hop
= ship
.game
->find_route(ship
.sector()->coord
,
793 plyrcli
.print("Continue with trip [yna]? ");
795 ship
.start_warp(TWCEventCallback(this, &TWCShipIf::post_warp
),
796 ship
.game
->get_sector(next_hop
));
805 void TWCShipIf::sector_scan()
807 plyrcli
.print("\n\e[1;33m-=- \e[0;35m %s \e[1;33m-=-\e[0;37m\n",
808 ship
.sector()->name());
811 ship
.sector()->find_player_ships(ships
, &ship
);
814 plyrcli
.print("Ships in this sector:\n");
815 TWCShipList::Iter
shi(ships
);
816 for (shi
.rewind(); shi
.next(); )
817 plyrcli
.print("%s%s <%s>, %s\n", shi
.ptr() == ship
.towing() ?
818 "<TOWING> " : "", shi().name(),
819 shi().shipclass()->name(),
820 shi().captain() ? shi().captain()->player
.name()
821 : WvString("unoccupied"));
824 TWCPort
*port
= ship
.sector()->port();
826 plyrcli
.print("A spaceport is in this sector: %s\n", port
->name());
830 void TWCGameController::initialize_ship(int ship
, int captain
)
832 UniConf
shipcfg(cfg
["ships"][ship
]);
833 shipcfg
["captain"].setmeint(captain
);
834 shipcfg
["shields up"].setmeint(1);
835 shipcfg
["weapon recharged"].setmeint(1);
839 bool TWCShipIf::checkmoveturns()
841 int move_cost
= ship
.shipclass()->move_cost();
843 move_cost
+= ship
.towing()->shipclass()->move_cost();
845 if (plyrcli
.player
.current_turns() < move_cost
)
847 plyrcli
.print("You don't have enough turns left.\n");
852 plyrcli
.player
.deduct_turns(move_cost
);
853 plyrcli
.print("%s turn%s deducted; %s remaining.\n", move_cost
,
854 move_cost
== 1 ? "" : "s",
855 plyrcli
.player
.current_turns());
856 plyrcli
.print("Warping...\n");
862 void TWCShipIf::tactical_menu_input()
864 char *line
= plyrcli
.getline(0);
873 plyrcli
.print(ship
.game
->screen("tactical"));
879 if (!ship
.weaponclass())
881 plyrcli
.print("You have no weapon on this ship.\n");
885 if (!ship
.weapon_charged())
887 plyrcli
.print("Your weapon is still charging.\n");
891 int attack_cost
= ship
.shipclass()->attack_cost();
892 if (plyrcli
.player
.current_turns() < attack_cost
)
894 plyrcli
.print("Sorry, you don't have any turns left.\n");
898 TWCShip
*target
= plyrcli
.acquire_target(
899 "Target which ship", "The ship is docked. You can wait until the "
900 "captain returns or attack the port itself."
905 plyrcli
.print("Power setting (1-100)? [100] ");
907 int power
= plyrcli
.get_int(abort
, 100);
911 if (power
< 1 || power
> 100)
913 plyrcli
.print("Power setting must be between 1 and 100.\n");
917 TWCAttackResults res
= ship
.shoot(target
, power
);
922 plyrcli
.print("Their shields are still holding, captain.\n");
926 plyrcli
.print("We destroyed them!\n");
934 plyrcli
.print("Deducting %s turn%s.\n", attack_cost
,
935 attack_cost
== 1 ? "" : "s");
936 plyrcli
.player
.deduct_turns(attack_cost
);
944 int target
= acquire_target("Demand surrender from which ship",
945 "This ship is currently docked. You can "
946 "wait for the captain to return.");
950 UniConf
targetcfg(cfg
["ships"][target
]);
951 int target_player_id
= targetcfg
["captain"].getmeint();
952 UniConf
targetpcfg(cfg
["players"][target_player_id
]);
954 TWCPlayerClient
*target_player
= game
->players
[target_player_id
];
957 print("The ship is unoccupied. You'll have to take it by force.\n");
961 msg("You demand the surrender of %s.\n", targetcfg
["name"].getme());
962 demanded_surrender
= target
;
963 target_player
->event(DemandSurrender
, loccfg().key().printable().num(),
965 WvString("\n%s demands you drop your shields and "
966 "surrender your cargo! You have 10 "
967 "seconds to comply.",
968 loccfg()["name"].getme()));
969 game
->events
.add_event(
970 TWCEventCallback(this, &TWCHumanPlayerClient::surrender_timeout
),