#include "../doomdef.h" #include "../info.h" #include "../g_game.h" #include "../k_objects.h" #include "../k_follower.h" #include "../m_random.h" #include "../p_local.h" #include "../p_setup.h" #include "../r_draw.h" // R_GetColorByName #include "../r_main.h" // R_PointToAngle2, R_PointToDist2 #include "../z_zone.h" // Z_StrDup/Z_Free // The following cannot be used due to conflicts with MT_EMBLEM. //#define audience_emblem_reserved_1(o) ((o)->reactiontime) #define audience_mainstate(o) ((o)->cvmem) #define audience_bobamp(o) ((o)->movefactor) #define audience_bobspeed(o) ((o)->cusval) #define audience_animoffset(o) ((o)->threshold) #define audience_focusplayer(o) ((o)->lastlook) #define audience_focusdelay(o) ((o)->movecount) void Obj_AudienceInit ( mobj_t * mobj, mapthing_t *mthing, INT32 followerpick) { INT16 *reflist = NULL; INT16 tempreflist[MAXHEADERFOLLOWERS]; UINT8 numref = 0; audience_mainstate(mobj) = S_NULL; // Pick follower if (mthing != NULL) { if (mthing->stringargs[0] != NULL) { // From mapthing char *stringcopy = Z_StrDup(mthing->stringargs[0]); char *tok = strtok(stringcopy, " ,"); char *c; // for erasing underscores numref = 0; while (tok && numref < MAXHEADERFOLLOWERS) { // Match follower name conversion for (c = tok; *c; c++) { if (*c != '_') continue; *c = ' '; } if ((tempreflist[numref++] = K_FollowerAvailable(tok)) == -1) CONS_Alert(CONS_WARNING, "Mapthing %s: Follower \"%s\" is invalid!\n", sizeu1(mthing-mapthings), tok); tok = strtok(NULL, " ,"); } Z_Free(stringcopy); reflist = tempreflist; } else { // From mapheader if (!mapheaderinfo[gamemap-1]) { // No mapheader, no shoes, no service. return; } numref = mapheaderinfo[gamemap-1]->numFollowers; reflist = mapheaderinfo[gamemap-1]->followers; } if (!numref || !reflist) { // This is the one thing a user should definitely be told about. CONS_Alert(CONS_WARNING, "Mapthing %s: Follower audience has no valid followers to pick from!\n", sizeu1(mthing-mapthings)); return; } followerpick = reflist[P_RandomKey(PR_RANDOMAUDIENCE, numref)]; if (followerpick < 0 || followerpick >= numfollowers) { // Is this user error or user choice..? P_RemoveMobj(mobj); return; } } // Handle storing follower properties on the object { mobj->destscale = FixedMul(3*mobj->destscale, followers[followerpick].scale); P_SetScale(mobj, mobj->destscale); audience_mainstate(mobj) = followers[followerpick].followstate; P_SetMobjState(mobj, audience_mainstate(mobj)); if (P_MobjWasRemoved(mobj)) return; // The following is derived from the default bobamp if (mobj->type != MT_EMBLEM && !(mobj->flags & MF_NOGRAVITY) && followers[followerpick].bobamp < 4*FRACUNIT) { audience_bobamp(mobj) = 4*mobj->scale; } else { audience_bobamp(mobj) = FixedMul(mobj->scale, followers[followerpick].bobamp); } audience_bobspeed(mobj) = followers[followerpick].bobspeed; audience_focusplayer(mobj) = MAXPLAYERS; if (P_RandomChance(PR_RANDOMAUDIENCE, FRACUNIT/2)) { audience_animoffset(mobj) = 5; } } // Handle colors if (mthing != NULL) { UINT16 colorpick = SKINCOLOR_NONE; if (mthing->stringargs[1] != NULL) { if (!stricmp("Random", mthing->stringargs[1])) { colorpick = FOLLOWERCOLOR_MATCH; } else { char *stringcopy = Z_StrDup(mthing->stringargs[1]); char *tok = strtok(stringcopy, " "); numref = 0; while (tok && numref < MAXHEADERFOLLOWERS) { if ((tempreflist[numref++] = R_GetColorByName(tok)) == SKINCOLOR_NONE) CONS_Alert(CONS_WARNING, "Mapthing %s: Follower color \"%s\" is invalid!\n", sizeu1(mthing-mapthings), tok); tok = strtok(NULL, " "); } Z_Free(stringcopy); if (numref) { colorpick = tempreflist[P_RandomKey(PR_RANDOMAUDIENCE, numref)]; } } } if (colorpick == SKINCOLOR_NONE || (colorpick >= numskincolors && colorpick != FOLLOWERCOLOR_MATCH && colorpick != FOLLOWERCOLOR_OPPOSITE)) { colorpick = followers[followerpick].defaultcolor; } if (colorpick >= numskincolors) { colorpick = P_RandomKey(PR_RANDOMAUDIENCE, numskincolors-1)+1; } mobj->color = colorpick; } } void Obj_AudienceThink ( mobj_t * mobj, boolean focusonplayer) { if (audience_mainstate(mobj) == S_NULL) { // Uninitialised, don't do anything funny. return; } if (focusonplayer == true) { if (audience_focusdelay(mobj) == 0) { fixed_t bestdist = INT32_MAX, dist; UINT8 i; audience_focusplayer(mobj) = MAXPLAYERS; for (i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] == false || players[i].spectator == true || P_MobjWasRemoved(players[i].mo)) continue; dist = R_PointToDist2( mobj->x, mobj->y, players[i].mo->x, players[i].mo->y ); if (dist >= bestdist) continue; dist = R_PointToDist2( mobj->z, 0, players[i].mo->z, dist ); if (dist >= bestdist) continue; bestdist = dist; audience_focusplayer(mobj) = i; } // Try to add some spacing out so the object isn't constantly looking for players audience_focusdelay(mobj) = TICRATE + min((bestdist/FRACUNIT), (2*TICRATE)) + (bestdist % TICRATE); } else { audience_focusdelay(mobj)--; } if (audience_focusplayer(mobj) < MAXPLAYERS && audience_focusplayer(mobj) >= 0) { angle_t diff = R_PointToAngle2( mobj->x, mobj->y, players[audience_focusplayer(mobj)].mo->x, players[audience_focusplayer(mobj)].mo->y ) - mobj->angle; boolean reverse = (diff >= ANGLE_180); if (reverse) diff = InvAngle(diff); if (diff > (ANG1*5)) diff /= 5; if (reverse) diff = InvAngle(diff); mobj->angle += diff; } } if (mobj->flags & MF_NOGRAVITY) { // This horrible calculation was inherited from k_follower.c mobj->sprzoff = FixedMul(audience_bobamp(mobj), FINESINE((( FixedMul(4 * M_TAU_FIXED, audience_bobspeed(mobj)) * (leveltime + audience_animoffset(mobj)) ) >> ANGLETOFINESHIFT) & FINEMASK)); // Offset to not go through floor... if (mobj->type == MT_EMBLEM) { ; // ...unless it's important to keep a centered hitbox } else if (mobj->flags2 & MF2_OBJECTFLIP) { mobj->sprzoff -= audience_bobamp(mobj); } else { mobj->sprzoff += audience_bobamp(mobj); } } else if (audience_animoffset(mobj) > 0) { // Skipped frames at spawn for offset in jumping audience_animoffset(mobj)--; } else if (audience_bobamp(mobj) == 0) { // Just sit there ; } else if (mobj->flags2 & MF2_OBJECTFLIP) { if (mobj->z + mobj->height >= mobj->ceilingz) { mobj->momz = -audience_bobamp(mobj); P_SetMobjState(mobj, audience_mainstate(mobj)); } } else if (mobj->z <= mobj->floorz) { mobj->momz = audience_bobamp(mobj); P_SetMobjState(mobj, audience_mainstate(mobj)); } }