Server:Boats

From The Al`Kabor Project Wiki
Revision as of 20:58, 29 December 2023 by Nazwadi (talk | contribs)
Jump to navigation Jump to search

For TAKP server, the boat routes use spawn conditions and are timed with grids. All synchronization has to be achieved via precise timing. Unfortunately, boats can't send send signals across zones. This is hard coded into the client.

If a grid is timed too short or too long, players will either get dropped off in the water or players will end up at the safe spot in the destination zone. Boat zones need to be static.

To skip straight to getting started, see #Boat_Route_Implementations

Server-Side Source

BoatID Constants

The following boats and their ID's are described as enum constants in the server source code: https://github.com/EQMacEmu/Server/blob/main/common/eq_constants.h#L685

//These are NPCIDs in the database. All of these boats send a BoardBoat opcode when boarded.
enum Boats
{
    Stormbreaker = 770, //freporte-oot-butcherblock
    SirensBane  = 771,
    Sea_King = 772, //erudext-erudsxing-qeynos
    Golden_Maiden  = 773,
    Maidens_Voyage  = 838, //timorous-firiona
    Bloated_Belly = 839, //timorous-overthere
    Barrel_Barge = 840, //Shuttle timorous-oasis
    Muckskimmer = 841,
    Sabrina = 24056, //Shuttle in Erud
    Island_Shuttle = 96075, //Shuttle to Elf docks in timorous
    Captains_Skiff = 842, //Shuttle timorous-butcherblock
    Icebreaker = 110083, //iceclad
    pirate_runners_skiff = 843 //Shuttle iceclad-nro
}; 

Boat ID's are stored in the player profile packet (pp.boatname and pp.boatid). When you board the boat, the boatid is registered in the PlayerProfile in the code (common/patches/mac_structs), sent by the client to the server,

struct PlayerProfile_Struct
{
 ...
 char    boat[32];
 ...
};

and temporarily stored in the `character_data`.boatid field in the database.

GM characters can observe player profile packets showing boats being boarded/departed by setting debug logging:

UPDATE logsys_categories SET log_to_gmsay = '3' WHERE log_category_description = 'Boats';

In order to see boat associated quest script debugging information, GM characters can see debug logging as well:

UPDATE logsys_categories SET log_to_gmsay = '3' WHERE log_category_description = 'Quest Debug';

Zone Entry

Each time a client enters a zone, the server checks the client packet's player profile struct for a boatid > 0 and whether the client is currently in Timorous Deep or Firionia Vie. If this is true, the boatid in the player profile struct is set back to 0.

void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
{
    ...
    PlayerProfile_Struct* pps = (PlayerProfile_Struct*) new uchar[sizeof(PlayerProfile_Struct) - 4];
    memcpy(pps, &m_pp, sizeof(PlayerProfile_Struct) - 4);
    ...
	if(m_pp.boatid > 0 && (zone->GetZoneID() == timorous || zone->GetZoneID() == firiona))
		pps->boat[0] = 0;

So when the boat moves, we send the pc to their destination and the boatid. If the player finds that boatid in their destination zone, it will land on it. Otherwise, it will be sent to the zone safe location.

Boat Route Implementations

Implementing a boat is achieved via entries to the database and the LUA scripting engine. The original implementation on TAKP for most of the boats was done by Cavedude (BB to Tim Deep shuttles was the only route he didn't finish).

Database Settings

The following database tables are used:

  • spawn2
  • spawngroup
  • spawnentry
  • spawn_conditions
  • grid_entries
  • grid
  • npc_types
npc_types

Boats are implemented as NPCs, so you'll find them in the npc_types table with a race value of Launch (73). For example,

INSERT INTO `npc_types` (`id`, `name`, `lastname`, `level`, `race`, `class`, `bodytype`, `hp`, `mana`, `gender`, `texture`, `helmtexture`, `size`, `hp_regen_rate`, `mana_regen_rate`, `loottable_id`, `merchant_id`, `npc_spells_id`, `npc_spells_effects_id`, `npc_faction_id`, `mindmg`, `maxdmg`, `attack_count`, `special_abilities`, `aggroradius`, `assistradius`, `face`, `luclin_hairstyle`, `luclin_haircolor`, `luclin_eyecolor`, `luclin_eyecolor2`, `luclin_beardcolor`, `luclin_beard`, `armortint_id`, `armortint_red`, `armortint_green`, `armortint_blue`, `d_melee_texture1`, `d_melee_texture2`, `prim_melee_type`, `sec_melee_type`, `ranged_type`, `runspeed`, `MR`, `CR`, `DR`, `FR`, `PR`, `see_invis`, `see_invis_undead`, `qglobal`, `AC`, `npc_aggro`, `spawn_limit`, `attack_delay`, `STR`, `STA`, `DEX`, `AGI`, `_INT`, `WIS`, `CHA`, `see_sneak`, `see_improved_hide`, `ATK`, `Accuracy`, `slow_mitigation`, `maxlevel`, `scalerate`, `private_corpse`, `unique_spawn_by_name`, `underwater`, `isquest`, `emoteid`, `spellscale`, `healscale`, `raid_target`, `chesttexture`, `armtexture`, `bracertexture`, `handtexture`, `legtexture`, `feettexture`, `light`, `walkspeed`, `combat_hp_regen`, `combat_mana_regen`, `aggro_pc`, `ignore_distance`, `encounter`, `ignore_despawn`, `avoidance`, `exp_pct`, `greed`, `engage_notice`, `stuck_behavior`, `flymode`) VALUES (846, 'Shuttle_I', '', 50, 73, 1, 5, 9550, 0, 0, 1, 0, 6, 287, 0, 0, 0, 0, 0, 0, 0, 0, -1, '19,1^20,1^24,1^35,1', 60, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 28, 28, 7, 1.25, 35, 35, 25, 35, 25, 0, 1, 0, 190, 0, 0, 30, 75, 75, 75, 75, 75, 75, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, 1.25, 6, 0, 0, 1000, 0, 0, 0, 100, 0, 0, 0, -1);
spawnentry
INSERT INTO `spawnentry` (`spawngroupID`, `npcID`, `chance`, `mintime`, `maxtime`, `expansion`, `min_expansion`, `max_expansion`) VALUES (448054, 849, 100, 0, 0, 0, 0, 0);
grid
INSERT INTO `grid` (`id`, `zoneid`, `type`, `type2`) VALUES (16, 68, 4, 1);

grid_entries

INSERT INTO `grid_entries` (`gridid`, `zoneid`, `number`, `x`, `y`, `z`, `heading`, `pause`, `centerpoint`) VALUES (16, 68, 11, 3595, 491, -11.9, 0, 0, 0);

LUA Scripting

Events

  • event_spawn
  • event_waypoint_arrive
  • event_waypoint_depart

Example Scripts