diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index d29af350..ff8fca3e 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4573,13 +4573,14 @@ struct G_CreateTelepipe_6x68 { } __packed__; // 6x69: NPC control +// Note: NPCs cannot be destroyed with 6x69; 6x1C is used instead for that. struct G_NPCControl_6x69 { G_UnusedHeader header; - le_uint16_t state = 0; - le_uint16_t npc_entity_id = 0; + le_uint16_t param1; // Commands 0/3: state; commands 1/2: npc_entity_id + le_uint16_t param2; // Commands 0/3: npc_entity_id; commands 1/2: unused le_uint16_t command = 0; // 0 = create follower NPC, 1 = stop acting, 2 = start acting, 3 = create attacker NPC - le_uint16_t npc_template_index = 0; // Specifies which NPC to create if command == 0 or 3; unused otherwise + le_uint16_t param3; // Commands 0/3: npc_template_index; commands 1/2: unused } __packed__; // 6x6A: Use boss warp (not valid on Episode 3) diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index bd073855..5c153f61 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1025,11 +1025,26 @@ static void on_npc_control(shared_ptr c, uint8_t command, uint8_t flag, // Don't allow NPC control commands if there is a player in the relevant slot const auto& l = c->require_lobby(); if (!l->is_game()) { - throw runtime_error("cannot create NPCs in the lobby"); + throw runtime_error("cannot create or modify NPC in the lobby"); } - if ((cmd.npc_entity_id < 4) && l->clients[cmd.npc_entity_id]) { - throw runtime_error("cannot overwrite existing player with NPC"); + + uint16_t npc_entity_id = 0xFFFF; + switch (cmd.command) { + case 0: + case 3: + npc_entity_id = cmd.param2; + break; + case 1: + case 2: + npc_entity_id = cmd.param1; + break; + default: + throw runtime_error("invalid 6x69 command"); } + if ((npc_entity_id < 4) && l->clients[npc_entity_id]) { + throw runtime_error("cannot create or modify NPC in existing player slot"); + } + forward_subcommand(c, command, flag, data, size); }