diff --git a/src/game/chr/chraction.c b/src/game/chr/chraction.c index 76fffd536..4de1c9faf 100644 --- a/src/game/chr/chraction.c +++ b/src/game/chr/chraction.c @@ -22736,7 +22736,7 @@ glabel var7f1a9260 /* f045b60: 00002825 */ or $a1,$zero,$zero /* f045b64: 14400008 */ bnez $v0,.L0f045b88 /* f045b68: 24050001 */ addiu $a1,$zero,0x1 -/* f045b6c: 0fc23922 */ jal doorActivate +/* f045b6c: 0fc23922 */ jal doorsRequestMode /* f045b70: 8e040004 */ lw $a0,0x4($s0) /* f045b74: 10000005 */ b .L0f045b8c /* f045b78: 8fbf002c */ lw $ra,0x2c($sp) diff --git a/src/game/chr/chrai.c b/src/game/chr/chrai.c index a427e262d..470d30a7f 100644 --- a/src/game/chr/chrai.c +++ b/src/game/chr/chrai.c @@ -111,7 +111,7 @@ bool (*g_CommandPointers[])(void) = { /*0x0062*/ aiIfObjectHealthy, /*0x0063*/ aiIfChrActivatedObject, /*0x0064*/ NULL, - /*0x0065*/ ai0065, + /*0x0065*/ aiObjInteract, /*0x0066*/ aiDestroyObject, /*0x0067*/ ai0067, /*0x0068*/ aiChrDropItems, diff --git a/src/game/chr/chraicommands.c b/src/game/chr/chraicommands.c index 53bcdc933..ef4c009f7 100644 --- a/src/game/chr/chraicommands.c +++ b/src/game/chr/chraicommands.c @@ -2198,14 +2198,14 @@ bool aiIfChrActivatedObject(void) /** * @cmd 0065 */ -bool ai0065(void) +bool aiObjInteract(void) { u8 *cmd = g_Vars.ailist + g_Vars.aioffset; struct defaultobj *obj = objFindByTagId(cmd[2]); if (obj && obj->prop) { if (obj->prop->type == PROPTYPE_DOOR) { - doorActivateWrapper(obj->prop, 0); + doorsActivate(obj->prop, false); } else if (obj->prop->type == PROPTYPE_OBJ || obj->prop->type == PROPTYPE_WEAPON) { propobjInteract(obj->prop); } @@ -2404,7 +2404,7 @@ bool aiOpenDoor(void) if (obj && obj->prop && obj->prop->type == PROPTYPE_DOOR) { if (!doorCallLift(obj->prop, false)) { struct doorobj *door = (struct doorobj *) obj; - doorActivate(door, DOORMODE_OPENING); + doorsRequestMode(door, DOORMODE_OPENING); } } @@ -2423,7 +2423,7 @@ bool aiCloseDoor(void) if (obj && obj->prop && obj->prop->type == PROPTYPE_DOOR) { struct doorobj *door = (struct doorobj *) obj; - doorActivate(door, DOORMODE_CLOSING); + doorsRequestMode(door, DOORMODE_CLOSING); } g_Vars.aioffset += 3; diff --git a/src/game/propobj.c b/src/game/propobj.c index 87e0d0608..5d8997e21 100644 --- a/src/game/propobj.c +++ b/src/game/propobj.c @@ -331,14 +331,15 @@ u32 var80069d00 = 0x00000000; * if the caller should proceed with opening or closing the door. A true return * doesn't necessarily mean the lift was called. * - * If onlyifclosed is false, the lift can be called even if the given door is - * open... which seems weird, because lift doors can't be open if the lift isn't - * already there. + * The allowclose argument determines whether the door should be closed if the + * lift is at the door. This is typically true when the player has activated the + * door, and false when NPCs have activated the door. If true, doorCallLift + * doesn't handle the activation which allows the caller to close the door. * * Lifts will not be called if it's occupied by anyone. This prevents chrs from * from calling lifts back when players are in them. */ -bool doorCallLift(struct prop *doorprop, bool onlyifclosed) +bool doorCallLift(struct prop *doorprop, bool allowclose) { struct doorobj *door = doorprop->door; bool handled = false; @@ -353,11 +354,15 @@ bool doorCallLift(struct prop *doorprop, bool onlyifclosed) handled = true; if (type == OBJTYPE_DOOR) { - // Unsure if reachable... I don't think the link->lift - // property is ever set to a door obj. - doorActivateWrapper(link->lift, onlyifclosed); + // This appears to be handling situations where the setup + // file specifies a door as the lift object. It activates + // that door, which then calls doorCallLift. This allows + // setup files to chain lift doors to other lift doors + // rather than directly to the lift, but this doesn't happen + // in practice so this branch is unused. + doorsActivate(link->lift, allowclose); } else if (type == OBJTYPE_LIFT) { - if (onlyifclosed && door->base.type == OBJTYPE_DOOR && !doorIsClosed(door)) { + if (allowclose && door->base.type == OBJTYPE_DOOR && !doorIsClosed(door)) { handled = false; } else { bool vacant = true; @@ -372,6 +377,10 @@ bool doorCallLift(struct prop *doorprop, bool onlyifclosed) if (vacant) { for (i = 0; i < numchrslots; i++) { + /** + * @bug: This is missing a chrIsDead check. + * If a chr dies in a lift it can no longer be called. + */ if (g_ChrSlots[i].prop && g_ChrSlots[i].lift == link->lift) { vacant = false; break; @@ -19004,7 +19013,7 @@ glabel var7f1aa43c .L0f077054: /* f077054: 14600005 */ bnez $v1,.L0f07706c /* f077058: 00000000 */ nop -/* f07705c: 0fc23922 */ jal doorActivate +/* f07705c: 0fc23922 */ jal doorsRequestMode /* f077060: 02002025 */ or $a0,$s0,$zero /* f077064: 1000002e */ b .L0f077120 /* f077068: 820a0084 */ lb $t2,0x84($s0) @@ -19056,7 +19065,7 @@ glabel var7f1aa43c /* f07710c: 820a0084 */ lb $t2,0x84($s0) .L0f077110: /* f077110: 02002025 */ or $a0,$s0,$zero -/* f077114: 0fc23922 */ jal doorActivate +/* f077114: 0fc23922 */ jal doorsRequestMode /* f077118: 24050002 */ addiu $a1,$zero,0x2 .L0f07711c: /* f07711c: 820a0084 */ lb $t2,0x84($s0) @@ -19108,7 +19117,7 @@ glabel var7f1aa43c /* f0771bc: 02002025 */ or $a0,$s0,$zero /* f0771c0: 10400003 */ beqz $v0,.L0f0771d0 /* f0771c4: 8fa40078 */ lw $a0,0x78($sp) -/* f0771c8: 0fc23fba */ jal doorActivateWrapper +/* f0771c8: 0fc23fba */ jal doorsActivate /* f0771cc: 00002825 */ or $a1,$zero,$zero .L0f0771d0: /* f0771d0: 3c0e800a */ lui $t6,%hi(g_Vars+0x8) @@ -19238,7 +19247,7 @@ glabel var7f1aa43c // } // // if (hasflag == false) { -// doorActivate(door, DOORMODE_CLOSING); +// doorsRequestMode(door, DOORMODE_CLOSING); // } else if (door->doorflags & DOORFLAG_0010) { // // Check if any sibling has a false return value // s32 pass = func0f08c040(door) == false; @@ -19259,7 +19268,7 @@ glabel var7f1aa43c // loopdoor = loopdoor->sibling; // } // } else { -// doorActivate(door, DOORMODE_CLOSING); +// doorsRequestMode(door, DOORMODE_CLOSING); // } // } // } @@ -19286,7 +19295,7 @@ glabel var7f1aa43c // if (door->doortype == DOORTYPE_8 // && doorIsClosed(door) // && doorIsPadlockFree(door)) { -// doorActivateWrapper(doorprop, false); +// doorsActivate(doorprop, false); // } // // // Update frac @@ -19932,7 +19941,7 @@ void liftTick(struct prop *prop) if (obj->flags & OBJFLAG_DEACTIVATED) { move = false; } else if (lift->doors[lift->levelcur] && !doorIsClosed(lift->doors[lift->levelcur])) { - doorActivate(lift->doors[lift->levelcur], DOORMODE_CLOSING); + doorsRequestMode(lift->doors[lift->levelcur], DOORMODE_CLOSING); move = false; } @@ -19996,7 +20005,7 @@ void liftTick(struct prop *prop) door = lift->doors[lift->levelcur]; if (door && door->keyflags == 0) { - doorActivate(door, DOORMODE_OPENING); + doorsRequestMode(door, DOORMODE_OPENING); } } @@ -25562,7 +25571,7 @@ glabel var7f1aa6e4 /* f07d704: 0fc24030 */ jal func0f0900c0 /* f07d708: e7ac0058 */ swc1 $f12,0x58($sp) /* f07d70c: 8fa401a0 */ lw $a0,0x1a0($sp) -/* f07d710: 0fc23922 */ jal doorActivate +/* f07d710: 0fc23922 */ jal doorsRequestMode /* f07d714: 24050001 */ addiu $a1,$zero,0x1 /* f07d718: c7ac0058 */ lwc1 $f12,0x58($sp) .L0f07d71c: @@ -26320,7 +26329,7 @@ glabel var7f1aa6e4 // // if (dist < 200 * 200) { // func0f0900c0(prop, door); -// doorActivate(door, DOORMODE_OPENING); +// doorsRequestMode(door, DOORMODE_OPENING); // } // // if (dist < 195 * 195) { @@ -42615,7 +42624,7 @@ glabel func0f08c190 .L0f08c3d0: /* f08c3d0: 12400003 */ beqz $s2,.L0f08c3e0 /* f08c3d4: 02c02025 */ or $a0,$s6,$zero -/* f08c3d8: 0fc23922 */ jal doorActivate +/* f08c3d8: 0fc23922 */ jal doorsRequestMode /* f08c3dc: 24050001 */ addiu $a1,$zero,0x1 .L0f08c3e0: /* f08c3e0: 86e20002 */ lh $v0,0x2($s7) @@ -44255,7 +44264,11 @@ void func0f08df10(s32 soundtype, struct prop *prop) } } -void func0f08e0c4(struct doorobj *door) +/** + * Play the door open sound, activate the door's portal, + * and configure the laser fade properties if it's a laser. + */ +void doorPrepareForOpen(struct doorobj *door) { door->base.flags &= ~OBJFLAG_DOOR_KEEPOPEN; door->base.hidden |= OBJHFLAG_00000200; @@ -44281,17 +44294,17 @@ void func0f08e0c4(struct doorobj *door) } } -void func0f08e1a0(struct doorobj *door) +/** + * Play the door close sound and configure the + * laser fade properties if it's a laser. + */ +void doorPrepareForClose(struct doorobj *door) { door->base.flags &= ~OBJFLAG_DOOR_KEEPOPEN; func0f08daa8(door->soundtype, door->base.prop); - if (door->doortype == DOORTYPE_LASER) { - door->fadetime60 = 60; - } else { - door->fadetime60 = 0; - } + door->fadetime60 = door->doortype == DOORTYPE_LASER ? 60 : 0; if (door->doortype == DOORTYPE_LASER) { door->laserfade = 0; @@ -44362,17 +44375,22 @@ void func0f08e2ac(struct doorobj *door) #endif } +/** + * Apply the given mode to an individual door (not its siblings). + * + * Handles playing door open/close sounds and activating the portal if opening. + */ void doorSetMode(struct doorobj *door, s32 newmode) { if (newmode == DOORMODE_OPENING) { if (door->mode == DOORMODE_IDLE || door->mode == DOORMODE_WAITING) { - func0f08e0c4(door); + doorPrepareForOpen(door); } door->mode = newmode; } else if (newmode == DOORMODE_CLOSING) { if (door->mode == DOORMODE_IDLE && door->frac > 0) { - func0f08e1a0(door); + doorPrepareForClose(door); } if ((door->mode != DOORMODE_IDLE && door->mode != DOORMODE_WAITING) || door->frac > 0) { @@ -44385,13 +44403,21 @@ void doorSetMode(struct doorobj *door, s32 newmode) } } -void doorActivate(struct doorobj *door, s32 newmode) +/** + * Request that the door and its siblings be applied the given mode + * (opening or closing). + * + * When opening an airlock-style door (eg. GE Dam gate), the requested mode is + * modified so that the sibling begins closing instead, and the main door waits + * for the sibling before it opens. + */ +void doorsRequestMode(struct doorobj *door, s32 newmode) { - struct doorobj *loopdoor; + struct doorobj *sibling; s32 siblingmode = newmode; - if ((door->base.flags2 & OBJFLAG2_40000000) && newmode == DOORMODE_OPENING) { + if ((door->base.flags2 & OBJFLAG2_AIRLOCKDOOR) && newmode == DOORMODE_OPENING) { siblingmode = DOORMODE_CLOSING; if (door->mode == DOORMODE_IDLE) { @@ -44401,11 +44427,11 @@ void doorActivate(struct doorobj *door, s32 newmode) doorSetMode(door, newmode); - loopdoor = door->sibling; + sibling = door->sibling; - while (loopdoor && loopdoor != door) { - doorSetMode(loopdoor, siblingmode); - loopdoor = loopdoor->sibling; + while (sibling && sibling != door) { + doorSetMode(sibling, siblingmode); + sibling = sibling->sibling; } } @@ -46208,20 +46234,30 @@ bool doorTestForInteract(struct prop *prop) return checkmore; } -void doorActivateWrapper(struct prop *doorprop, bool arg1) +/** + * Activate the doors by calling the lift or requesting the new door mode + * (opening/closing) for the given door and its siblings. + * + * Assumes any lock checks have already been done and have passed. + * + * The allowliftclose argument determines whether the door should be closed if + * it's a lift door and the lift is at the door. This is typically true when the + * player has activated the door, and false when NPCs have activated the door. + */ +void doorsActivate(struct prop *doorprop, bool allowliftclose) { struct doorobj *door = doorprop->door; - if (!doorCallLift(doorprop, arg1)) { + if (!doorCallLift(doorprop, allowliftclose)) { if (door->mode == DOORMODE_OPENING || door->mode == DOORMODE_WAITING) { - doorActivate(door, DOORMODE_CLOSING); + doorsRequestMode(door, DOORMODE_CLOSING); } else if (door->mode == DOORMODE_CLOSING) { - doorActivate(door, DOORMODE_OPENING); + doorsRequestMode(door, DOORMODE_OPENING); } else if (door->mode == DOORMODE_IDLE) { if (door->frac > 0.5f * door->maxfrac) { - doorActivate(door, DOORMODE_CLOSING); + doorsRequestMode(door, DOORMODE_CLOSING); } else { - doorActivate(door, DOORMODE_OPENING); + doorsRequestMode(door, DOORMODE_OPENING); } } } @@ -46359,7 +46395,7 @@ bool propdoorInteract(struct prop *doorprop) if (func0f08bd00(playerprop, doorprop)) { func0f0900c0(playerprop, door); - doorActivateWrapper(doorprop, 1); + doorsActivate(doorprop, true); } else if (door->mode == DOORMODE_IDLE && door->frac < 0.5f * door->maxfrac) { if ((door->base.flags2 & OBJFLAG2_00000004) == 0) { struct textoverride *override = invGetTextOverrideForObj(&door->base); diff --git a/src/game/training/training.c b/src/game/training/training.c index 295cd7079..166116757 100644 --- a/src/game/training/training.c +++ b/src/game/training/training.c @@ -1621,7 +1621,7 @@ void frCloseAndLockDoor(void) if (obj && obj->prop && obj->prop->type == PROPTYPE_DOOR) { struct doorobj *door = (struct doorobj *)obj; door->keyflags |= 0x40; - doorActivate(door, DOORMODE_CLOSING); + doorsRequestMode(door, DOORMODE_CLOSING); } } diff --git a/src/include/commands.h b/src/include/commands.h index 6084b3793..f1629d98f 100644 --- a/src/include/commands.h +++ b/src/include/commands.h @@ -945,8 +945,12 @@ object, \ label, -// Unused, and no idea what it does. -#define cmd0065(object) \ +/** + * Interacts with the given object, as if the player pressed B on it. + * + * If the object is a door, lock checks are skipped. + */ +#define obj_interact(object) \ mkshort(0x0065), \ object, diff --git a/src/include/constants.h b/src/include/constants.h index 2e95d721d..ca483cc72 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -2522,7 +2522,7 @@ #define OBJFLAG2_LOCKEDFRONT 0x08000000 // One-way door lock #define OBJFLAG2_LOCKEDBACK 0x10000000 // One-way door lock #define OBJFLAG2_AICANNOTUSE 0x20000000 -#define OBJFLAG2_40000000 0x40000000 // Used by doors +#define OBJFLAG2_AIRLOCKDOOR 0x40000000 // Door waits for sibling to close before it can open #define OBJFLAG2_80000000 0x80000000 // Attack Ship glass // obj->flags3 diff --git a/src/include/game/chr/chraicommands.h b/src/include/game/chr/chraicommands.h index 218aaaaac..16e1bcf84 100644 --- a/src/include/game/chr/chraicommands.h +++ b/src/include/game/chr/chraicommands.h @@ -104,7 +104,7 @@ /*0x0061*/ bool aiIfGunUnclaimed(void); /*0x0062*/ bool aiIfObjectHealthy(void); /*0x0063*/ bool aiIfChrActivatedObject(void); -/*0x0065*/ bool ai0065(void); +/*0x0065*/ bool aiObjInteract(void); /*0x0066*/ bool aiDestroyObject(void); /*0x0067*/ bool ai0067(void); /*0x0068*/ bool aiChrDropItems(void); diff --git a/src/include/game/propobj.h b/src/include/game/propobj.h index b608fe136..413ef8422 100644 --- a/src/include/game/propobj.h +++ b/src/include/game/propobj.h @@ -27,7 +27,7 @@ void countdownTimerSetRunning(bool running); void countdownTimerSetValue(f32 frames); void countdownTimerSetVisible(u32 flag, bool show); void countdownTimerTick(void); -bool doorCallLift(struct prop *doorprop, bool onlyifclosed); +bool doorCallLift(struct prop *doorprop, bool allowclose); bool doorIsPadlockFree(struct doorobj *door); bool objPassesSafePickupChecks(struct defaultobj *obj); void objUpdateLinkedScenery(struct defaultobj *obj); @@ -314,13 +314,13 @@ void func0f08d784(s32 soundtype, struct prop *prop); void func0f08daa8(s32 soundtype, struct prop *prop); void func0f08dd44(s32 soundtype, struct prop *prop); void func0f08df10(s32 soundtype, struct prop *prop); -void func0f08e0c4(struct doorobj *door); -void func0f08e1a0(struct doorobj *door); +void doorPrepareForOpen(struct doorobj *door); +void doorPrepareForClose(struct doorobj *door); u32 decodeXorAaaaaaaa(u32 value); void func0f08e224(struct doorobj *door); void func0f08e2ac(struct doorobj *door); void doorSetMode(struct doorobj *door, s32 newmode); -void doorActivate(struct doorobj *door, s32 newmode); +void doorsRequestMode(struct doorobj *door, s32 newmode); s32 doorIsClosed(struct doorobj *door); s32 doorIsOpen(struct doorobj *door); s32 func0f08e5a8(s16 *rooms, struct screenbox *box); @@ -335,7 +335,7 @@ u32 func0f08f538(void); u32 func0f08f604(void); bool func0f08f968(struct doorobj *door, bool arg1); bool doorTestForInteract(struct prop *prop); -void doorActivateWrapper(struct prop *prop, bool arg1); +void doorsActivate(struct prop *prop, bool allowliftclose); u32 func0f08fffc(void); void func0f0900c0(struct prop *prop, struct doorobj *door); bool propdoorInteract(struct prop *doorprop);