diff --git a/gameSource/EditorObjectPage.cpp b/gameSource/EditorObjectPage.cpp index 4381cdf9b..be9c864e0 100644 --- a/gameSource/EditorObjectPage.cpp +++ b/gameSource/EditorObjectPage.cpp @@ -168,8 +168,11 @@ EditorObjectPage::EditorObjectPage() mPersonNoSpawnCheckbox( 290, -150, 2 ), // these are in same spot because they're never shown at same time mMaleCheckbox( 290, -190, 2 ), - mDeathMarkerCheckbox( 290, -190, 2 ), - mHomeMarkerCheckbox( 100, -120, 2 ), + mDeathMarkerCheckbox( 635, -270, 2 ), + mHomeMarkerCheckbox( 635, -290, 2 ), + mTapoutTriggerCheckbox( 635, -310, 2 ), + mTapoutTriggerField( smallFont, 602, -334, 8, false, "", + "0123456789,", NULL ), mFloorCheckbox( 635, -210, 2 ), mPartialFloorCheckbox( 635, -230, 2 ), mHeldInHandCheckbox( 290, 36, 2 ), @@ -662,6 +665,14 @@ EditorObjectPage::EditorObjectPage() mHomeMarkerCheckbox.setVisible( true ); mHomeMarkerCheckbox.addActionListener( this ); + addComponent( &mTapoutTriggerCheckbox ); + mTapoutTriggerCheckbox.setVisible( true ); + mTapoutTriggerCheckbox.addActionListener( this ); + addComponent( &mTapoutTriggerField ); + mTapoutTriggerField.setVisible( false ); + mTapoutTriggerField.addActionListener( this ); + mTapoutTriggerField.setText( "" ); + addComponent( &mFloorCheckbox ); mFloorCheckbox.setVisible( true ); @@ -988,6 +999,9 @@ void EditorObjectPage::updateAgingPanel() { mDeathMarkerCheckbox.setVisible( true ); mHomeMarkerCheckbox.setVisible( true ); + mTapoutTriggerCheckbox.setVisible( true ); + if( mTapoutTriggerCheckbox.getToggled() ) + mTapoutTriggerField.setVisible( true ); if( ! mContainSizeField.isVisible() ) { mFloorCheckbox.setVisible( true ); @@ -1029,6 +1043,10 @@ void EditorObjectPage::updateAgingPanel() { mHomeMarkerCheckbox.setToggled( false ); mHomeMarkerCheckbox.setVisible( false ); + mTapoutTriggerCheckbox.setToggled( false ); + mTapoutTriggerCheckbox.setVisible( false ); + mTapoutTriggerField.setVisible( false ); + mFloorCheckbox.setToggled( false ); mFloorCheckbox.setVisible( false ); mPartialFloorCheckbox.setToggled( false ); @@ -1540,6 +1558,8 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { int maxPickupAge = 9999999; sscanf( pickupAgeText, "%d,%d", &( minPickupAge ), &( maxPickupAge ) ); + char *tapoutTriggerParameters = mTapoutTriggerField.getText(); + int newID = addObject( text, mCheckboxes[0]->getToggled(), @@ -1569,6 +1589,8 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { race, mDeathMarkerCheckbox.getToggled(), mHomeMarkerCheckbox.getToggled(), + mTapoutTriggerCheckbox.getToggled(), + tapoutTriggerParameters, mFloorCheckbox.getToggled(), mPartialFloorCheckbox.getToggled(), mFloorHuggingCheckbox.getToggled(), @@ -1631,6 +1653,7 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { delete [] text; delete [] biomes; + delete [] tapoutTriggerParameters; mSpritePicker.unselectObject(); @@ -1714,6 +1737,8 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { int maxPickupAge = 9999999; sscanf( pickupAgeText, "%d,%d", &( minPickupAge ), &( maxPickupAge ) ); + char *tapoutTriggerParameters = mTapoutTriggerField.getText(); + addObject( text, mCheckboxes[0]->getToggled(), @@ -1743,6 +1768,8 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { race, mDeathMarkerCheckbox.getToggled(), mHomeMarkerCheckbox.getToggled(), + mTapoutTriggerCheckbox.getToggled(), + tapoutTriggerParameters, mFloorCheckbox.getToggled(), mPartialFloorCheckbox.getToggled(), mFloorHuggingCheckbox.getToggled(), @@ -1801,6 +1828,7 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { delete [] text; delete [] biomes; + delete [] tapoutTriggerParameters; mSpritePicker.unselectObject(); @@ -1871,6 +1899,8 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { mSlotsGroundCheckbox.setVisible( false ); mSlotsLockedCheckbox.setVisible( false ); mSlotsNoSwapCheckbox.setVisible( false ); + + mTapoutTriggerField.setText( "" ); mFloorCheckbox.setToggled( false ); mFloorCheckbox.setVisible( true ); @@ -2494,6 +2524,14 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { mPartialFloorCheckbox.setToggled( false ); } } + else if( inTarget == &mTapoutTriggerCheckbox ) { + if( mTapoutTriggerCheckbox.getToggled() ) { + mTapoutTriggerField.setVisible( true ); + } + else { + mTapoutTriggerField.setVisible( false ); + } + } else if( inTarget == &mHeldInHandCheckbox ) { if( mHeldInHandCheckbox.getToggled() ) { mRideableCheckbox.setToggled( false ); @@ -3425,6 +3463,17 @@ void EditorObjectPage::actionPerformed( GUIComponent *inTarget ) { mMaleCheckbox.setToggled( pickedRecord->male ); mDeathMarkerCheckbox.setToggled( pickedRecord->deathMarker ); mHomeMarkerCheckbox.setToggled( pickedRecord->homeMarker ); + mTapoutTriggerCheckbox.setToggled( pickedRecord->isTapOutTrigger ); + if( mTapoutTriggerCheckbox.getToggled() ) { + mTapoutTriggerField.setVisible( true ); + char *tapoutTriggerParametersText = getTapoutTriggerString( pickedRecord ); + mTapoutTriggerField.setText( tapoutTriggerParametersText ); + delete [] tapoutTriggerParametersText; + } + else { + mTapoutTriggerField.setVisible( false ); + mTapoutTriggerField.setText( "" ); + } mFloorCheckbox.setToggled( pickedRecord->floor ); mPartialFloorCheckbox.setToggled( pickedRecord->noCover ); @@ -4795,8 +4844,8 @@ void EditorObjectPage::draw( doublePair inViewCenter, if( mDeathMarkerCheckbox.isVisible() ) { pos = mDeathMarkerCheckbox.getPosition(); - pos.y += checkboxSep + 5; - smallFont->drawString( "Death", pos, alignCenter ); + pos.x -= checkboxSep; + smallFont->drawString( "Death", pos, alignRight ); } if( mHomeMarkerCheckbox.isVisible() ) { @@ -4805,6 +4854,12 @@ void EditorObjectPage::draw( doublePair inViewCenter, smallFont->drawString( "Home", pos, alignRight ); } + if( mTapoutTriggerCheckbox.isVisible() ) { + pos = mTapoutTriggerCheckbox.getPosition(); + pos.x -= checkboxSep; + smallFont->drawString( "TapoutTrigger", pos, alignRight ); + } + if( mFloorCheckbox.isVisible() ) { pos = mFloorCheckbox.getPosition(); pos.x -= checkboxSep; diff --git a/gameSource/EditorObjectPage.h b/gameSource/EditorObjectPage.h index dd2b8d25b..5dfb156ec 100644 --- a/gameSource/EditorObjectPage.h +++ b/gameSource/EditorObjectPage.h @@ -163,6 +163,8 @@ class EditorObjectPage : public GamePage, public ActionListener { CheckboxButton mMaleCheckbox; CheckboxButton mDeathMarkerCheckbox; CheckboxButton mHomeMarkerCheckbox; + CheckboxButton mTapoutTriggerCheckbox; + TextField mTapoutTriggerField; CheckboxButton mFloorCheckbox; CheckboxButton mPartialFloorCheckbox; diff --git a/gameSource/EditorSpriteTrimPage.cpp b/gameSource/EditorSpriteTrimPage.cpp index 13acca8aa..d5f97e907 100644 --- a/gameSource/EditorSpriteTrimPage.cpp +++ b/gameSource/EditorSpriteTrimPage.cpp @@ -615,6 +615,8 @@ void EditorSpriteTrimPage::actionPerformed( GUIComponent *inTarget ) { false, false, false, + NULL, + false, false, false, false, diff --git a/gameSource/LivingLifePage.cpp b/gameSource/LivingLifePage.cpp index 531adf15d..a528c20e6 100644 --- a/gameSource/LivingLifePage.cpp +++ b/gameSource/LivingLifePage.cpp @@ -213,6 +213,7 @@ unsigned char objectSearchPanelToggleKey = 'j'; char familyDisplayEnabled = false; unsigned char familyDisplayPanelToggleKey = 'p'; char dangerousTileEnabled = false; +char alwaysShowPlayerLabelEnabled = false; static JenkinsRandomSource randSource( 340403 ); static JenkinsRandomSource remapRandSource( 340403 ); @@ -5125,7 +5126,8 @@ void LivingLifePage::drawChalkBackgroundString( doublePair inPos, int inForceMinChalkBlots, FloatColor *inForceBlotColor, FloatColor *inForceTextColor, - bool tinyStyle ) { + bool tinyStyle, + bool allowOffscreen ) { char *stringUpper = stringToUpperCase( inString ); @@ -5152,12 +5154,12 @@ void LivingLifePage::drawChalkBackgroundString( doublePair inPos, double firstLineY = inPos.y + ( lines->size() - 1 ) * lineSpacing; - if( firstLineY > lastScreenViewCenter.y + recalcOffsetY( 330 ) * gui_fov_scale ) { + if( !allowOffscreen && firstLineY > lastScreenViewCenter.y + recalcOffsetY( 330 ) * gui_fov_scale ) { // off top of screen firstLineY = lastScreenViewCenter.y + recalcOffsetY( 330 ) * gui_fov_scale; } - if( inPos.y < lastScreenViewCenter.y - recalcOffsetY( 280 ) * gui_fov_scale ) { + if( !allowOffscreen && inPos.y < lastScreenViewCenter.y - recalcOffsetY( 280 ) * gui_fov_scale ) { // off bottom of screen double lastLineY = lastScreenViewCenter.y - recalcOffsetY( 280 ) * gui_fov_scale; @@ -11434,80 +11436,6 @@ void LivingLifePage::draw( doublePair inViewCenter, - // Player labels - int playerLabelSec = 3; - int playerLabelFadeSec = 1; - if( game_getCurrentTime() - lastHoverPlayerTime > playerLabelSec + playerLabelFadeSec ) { - lastHoverPlayerID = 0; - } - - if( lastHoverPlayerID != 0 ) { - - LiveObject *o = getLiveObject( lastHoverPlayerID ); - - if( o != NULL && o->heldByAdultID == -1 ) { - - float delta = (float)(game_getCurrentTime() - lastHoverPlayerTime); - float fade = 1.0; - if( delta > playerLabelSec ) - fade = (playerLabelSec + playerLabelFadeSec - delta) / playerLabelFadeSec; - - - char *name = NULL; - char infertilityTagPresent = false; - if( o->name != NULL ) { - name = stringDuplicate(o->name); - infertilityTagPresent = stripFertilitySuffix( name ); - if( name[0] == '\0' ) { - delete [] name; - name = NULL; - } - } - char *infertilityString = NULL; - if( infertilityTagPresent ) { - infertilityString = stringDuplicate( " (INFERTILE)" ); - } - else { - infertilityString = stringDuplicate( "" ); - } - - double age = computeServerAge( computeCurrentAge( o ) ); - - char *label; - if( name != NULL && o->relationName != NULL ) { - label = autoSprintf("%s %.1f, %s%s", name, age, o->relationName, infertilityString); - } - else if( name == NULL && o->relationName != NULL ) { - label = autoSprintf("%s %.1f%s", o->relationName, age, infertilityString); - } - else if( name != NULL && o->relationName == NULL ) { - label = autoSprintf("%s %.1f%s", name, age, infertilityString); - } - else { - label = autoSprintf("UNNAMED STRANGER %.1f%s", age, infertilityString); - } - - FloatColor bgColor = { 0.05, 0.05, 0.05, 1.0 }; - FloatColor txtColor = { 1, 1, 1, 1 }; - - doublePair labelPos = mult( o->currentPos, CELL_D ); - - double labelWidth = tinyHandwritingFont->measureString( label ); - labelPos.x -= labelWidth / 2; - labelPos.y -= 16; - - drawChalkBackgroundString( - labelPos, - label, fade, 100000.0, o, -1, &bgColor, &txtColor, true ); - - if( name != NULL ) delete [] name; - delete [] label; - - } - - } - - // cursor-tips if( !isAnyUIHovered() ) if( ourLiveObject != NULL ) { @@ -11938,6 +11866,137 @@ void LivingLifePage::draw( doublePair inViewCenter, } } + + // Player labels + int playerLabelSec = 3; + int playerLabelFadeSec = 1; + if( game_getCurrentTime() - lastHoverPlayerTime > playerLabelSec + playerLabelFadeSec ) { + lastHoverPlayerID = 0; + } + + if( alwaysShowPlayerLabelEnabled ) { + int showLabelRange = 50; + + for( int i=0; iheldByAdultID != -1 ) continue; + if ( o->id == lastHoverPlayerID ) continue; + if ( o->outOfRange ) continue; + int distX = o->xd - ourLiveObject->xd; + if ( distX > showLabelRange || distX < -showLabelRange) continue; + int distY = o->yd - ourLiveObject->yd; + if ( distY > showLabelRange || distY < -showLabelRange) continue; + + char *name = NULL; + char infertilityTagPresent = false; + if( o->name != NULL ) { + name = stringDuplicate(o->name); + infertilityTagPresent = stripFertilitySuffix( name ); + if( name[0] == '\0' ) { + delete [] name; + name = NULL; + } + } + + char *label; + if( name != NULL ) { + label = autoSprintf("%s", name); + } + else if( o->relationName != NULL ) { + label = autoSprintf("%s", o->relationName); + } + else { + label = autoSprintf("UNNAMED STRANGER"); + } + + FloatColor bgColor = { 0.05, 0.05, 0.05, 1.0 }; + FloatColor txtColor = { 1, 1, 1, 1 }; + + doublePair labelPos = mult( o->currentPos, CELL_D ); + + double labelWidth = tinyHandwritingFont->measureString( label ); + labelPos.x -= labelWidth / 2; + labelPos.y -= 16; + + drawChalkBackgroundString( + labelPos, + label, 1.0, 100000.0, o, -1, &bgColor, &txtColor, true, true ); + + if( name != NULL ) delete [] name; + delete [] label; + + } + + } + + if( lastHoverPlayerID != 0 ) { + + LiveObject *o = getLiveObject( lastHoverPlayerID ); + + if( o != NULL && o->heldByAdultID == -1 ) { + + float delta = (float)(game_getCurrentTime() - lastHoverPlayerTime); + float fade = 1.0; + if( delta > playerLabelSec ) + fade = (playerLabelSec + playerLabelFadeSec - delta) / playerLabelFadeSec; + if( alwaysShowPlayerLabelEnabled ) fade = 1.0; + + char *name = NULL; + char infertilityTagPresent = false; + if( o->name != NULL ) { + name = stringDuplicate(o->name); + infertilityTagPresent = stripFertilitySuffix( name ); + if( name[0] == '\0' ) { + delete [] name; + name = NULL; + } + } + char *infertilityString = NULL; + if( infertilityTagPresent ) { + infertilityString = stringDuplicate( " (INFERTILE)" ); + } + else { + infertilityString = stringDuplicate( "" ); + } + + double age = computeServerAge( computeCurrentAge( o ) ); + + char *label; + if( name != NULL && o->relationName != NULL ) { + label = autoSprintf("%s %.1f, %s%s", name, age, o->relationName, infertilityString); + } + else if( name == NULL && o->relationName != NULL ) { + label = autoSprintf("%s %.1f%s", o->relationName, age, infertilityString); + } + else if( name != NULL && o->relationName == NULL ) { + label = autoSprintf("%s %.1f%s", name, age, infertilityString); + } + else { + label = autoSprintf("UNNAMED STRANGER %.1f%s", age, infertilityString); + } + + FloatColor bgColor = { 0.05, 0.05, 0.05, 1.0 }; + FloatColor txtColor = { 1, 1, 1, 1 }; + + doublePair labelPos = mult( o->currentPos, CELL_D ); + + double labelWidth = tinyHandwritingFont->measureString( label ); + labelPos.x -= labelWidth / 2; + labelPos.y -= 16; + + drawChalkBackgroundString( + labelPos, + label, fade, 100000.0, o, -1, &bgColor, &txtColor, true ); + + if( name != NULL ) delete [] name; + delete [] label; + + } + + } + + // debug info on cursor if( debugMode ) { @@ -25451,7 +25510,10 @@ void LivingLifePage::pointerDown( float inX, float inY ) { // if holding something, and this is a set-down action // show click reaction if( modClick && - ourLiveObject->holdingID != 0 ) { + (ourLiveObject->holdingID != 0 || + // or if we're shift right clicking on a floor with empty hand + (isShiftKeyDown() && floorDestID > 0) ) + ) { int mapI = mapY * mMapD + mapX; @@ -26005,7 +26067,9 @@ void LivingLifePage::pointerDown( float inX, float inY ) { // up ( ourLiveObject->holdingID != 0 || ( destObjInClickedTile > 0 && - ! destObjInClickedTilePermanent ) ) ) + ! destObjInClickedTilePermanent ) || + // or if we're shift right clicking on a floor with empty hand + ( isShiftKeyDown() && floorDestID > 0 ) ) ) || killMode || tryingToPickUpBaby || useOnBabyLater @@ -26317,7 +26381,23 @@ void LivingLifePage::pointerDown( float inX, float inY ) { // check for other special case // a use-on-ground transition or use-on-floor transition - if( ourLiveObject->holdingID > 0 ) { + if( isShiftKeyDown() && floorDestID > 0 && + destObjInClickedTile == 0 && + ourLiveObject->holdingID == 0 + ) { + // check if bare-hand transition on floor exists + TransRecord *r = + getTrans( 0, floorDestID ); + + if( r != NULL ) { + // a bare-hand transition exists! + + // override the drop action + action = "USE"; + + } + } + else if( ourLiveObject->holdingID > 0 ) { ObjectRecord *held = getObject( ourLiveObject->holdingID ); @@ -27289,6 +27369,14 @@ void LivingLifePage::keyDown( unsigned char inASCII ) { vogPickerOn = ! vogPickerOn; } break; + case 'n': + if( ! mSayField.isFocused() && !vogMode && + !commandKey && !shiftKey ) { + alwaysShowPlayerLabelEnabled = !alwaysShowPlayerLabelEnabled; + SettingsManager::setSetting("alwaysShowPlayerLabelEnabled", + alwaysShowPlayerLabelEnabled); + } + break; case 'N': if( ! mSayField.isFocused() && serverSocketConnected && diff --git a/gameSource/LivingLifePage.h b/gameSource/LivingLifePage.h index 312b1d96d..78aff02c5 100644 --- a/gameSource/LivingLifePage.h +++ b/gameSource/LivingLifePage.h @@ -1028,7 +1028,8 @@ class LivingLifePage : public GamePage, public ActionListener { int inForceMinChalkBlots = -1, FloatColor *inForceBlotColor = NULL, FloatColor *inForceTextColor = NULL, - bool tinyStyle = false ); + bool tinyStyle = false, + bool allowOffscreen = false ); void drawOffScreenSounds(); diff --git a/gameSource/SettingsPage.cpp b/gameSource/SettingsPage.cpp index 5c8a98f14..e4c54350e 100644 --- a/gameSource/SettingsPage.cpp +++ b/gameSource/SettingsPage.cpp @@ -41,6 +41,7 @@ extern char objectSearchEnabled; extern char familyDisplayEnabled; extern char dangerousTileEnabled; extern char showPipsOfFoodHeld; +extern char alwaysShowPlayerLabelEnabled; extern char *commandShortcutsRawText; @@ -133,7 +134,8 @@ SettingsPage::SettingsPage() mEnableFamilyDisplayBox(0, -32, 4), mEnableDangerousTileBox(0, -72, 4), mGenerateTownPlannerMapsBox( 561, 52, 4 ), - mEnableShowingHeldFoodPips( 561, 52, 4 ) { + mEnableShowingHeldFoodPips( 561, 52, 4 ), + mEnableAlwaysShowPlayerLabelsBox( 561, 52, 4 ) { @@ -142,6 +144,8 @@ SettingsPage::SettingsPage() addComponent( &mBackground ); // Advanced + addComponent( &mEnableAlwaysShowPlayerLabelsBox ); + mEnableAlwaysShowPlayerLabelsBox.addActionListener( this ); addComponent( &mEnableShowingHeldFoodPips ); mEnableShowingHeldFoodPips.addActionListener( this ); addComponent( &mGenerateTownPlannerMapsBox ); @@ -353,6 +357,7 @@ SettingsPage::SettingsPage() mEnableDangerousTileBox.setCursorTip( "HIGHLIGHT DANGEROUS TILES AND BLOCK PATHING INTO THEM ON KEYBOARD." ); mGenerateTownPlannerMapsBox.setCursorTip( "SAVE MAP FILES TO BE USED IN TOWN PLANNER" ); mEnableShowingHeldFoodPips.setCursorTip( "SHOW FOOD PIPS OF THE FOOD YOU'RE HOLDING" ); + mEnableAlwaysShowPlayerLabelsBox.setCursorTip( "ALWAYS SHOW PLAYER NAME LABELS" ); mOldFullscreenSetting = SettingsManager::getIntSetting( "fullscreen", 1 ); @@ -478,6 +483,10 @@ SettingsPage::SettingsPage() showPipsOfFoodHeld = SettingsManager::getIntSetting( "showPipsOfFoodHeldEnabled", 0 ); mEnableShowingHeldFoodPips.setToggled( showPipsOfFoodHeld ); + + alwaysShowPlayerLabelEnabled = SettingsManager::getIntSetting("alwaysShowPlayerLabelEnabled", 0); + + mEnableAlwaysShowPlayerLabelsBox.setToggled( alwaysShowPlayerLabelEnabled ); @@ -964,6 +973,13 @@ void SettingsPage::actionPerformed( GUIComponent *inTarget ) { if( newSetting ) showPipsOfFoodHeld = true; SettingsManager::setSetting( "showPipsOfFoodHeldEnabled", newSetting ); } + else if ( inTarget == &mEnableAlwaysShowPlayerLabelsBox ) { + int newSetting = mEnableAlwaysShowPlayerLabelsBox.getToggled(); + alwaysShowPlayerLabelEnabled = false; + if( newSetting ) alwaysShowPlayerLabelEnabled = true; + SettingsManager::setSetting("alwaysShowPlayerLabelEnabled", + newSetting); + } checkRestartRequired(); updatePage(); @@ -1261,6 +1277,13 @@ void SettingsPage::draw( doublePair inViewCenter, drawTextWithShadow("SHOW HELD FOOD PIPS", pos, alignRight); } + if (mEnableAlwaysShowPlayerLabelsBox.isVisible()) { + doublePair pos = mEnableAlwaysShowPlayerLabelsBox.getPosition(); + pos.x -= 30; + pos.y -= 2; + + drawTextWithShadow("ALWAYS SHOW NAMES", pos, alignRight); + } } @@ -1331,6 +1354,8 @@ void SettingsPage::makeActive( char inFresh ) { mMusicStartTime = 0; mUISizeSlider.setValue( 1 / gui_fov_target_scale_hud ); + + mEnableAlwaysShowPlayerLabelsBox.setToggled( alwaysShowPlayerLabelEnabled ); int tryCount = 0; @@ -1414,16 +1439,17 @@ void SettingsPage::updatePage() { mDiscordHideFirstNameInDetails.setPosition(0, -lineSpacing); #endif // USE_DISCORD - mCommandShortcuts.setPosition(180 - 16, lineSpacing * 4); - mEnableAdvancedShowUseOnObjectHoverKeybind.setPosition(0, lineSpacing * 3); - mEnableCoordinatesBox.setPosition(0, lineSpacing * 2); - mEnablePersistentEmoteBox.setPosition(0, lineSpacing * 1); - mEnableYumFinderBox.setPosition(0, lineSpacing * 0); - mEnableObjectSearchBox.setPosition(0, lineSpacing * -1); - mEnableFamilyDisplayBox.setPosition(0, lineSpacing * -2); - mEnableDangerousTileBox.setPosition(0, lineSpacing * -3); - mGenerateTownPlannerMapsBox.setPosition(0, lineSpacing * -4); - mEnableShowingHeldFoodPips.setPosition(0, lineSpacing * -5); + mCommandShortcuts.setPosition(180 - 16, lineSpacing * 5); + mEnableAdvancedShowUseOnObjectHoverKeybind.setPosition(0, lineSpacing * 4); + mEnableCoordinatesBox.setPosition(0, lineSpacing * 3); + mEnablePersistentEmoteBox.setPosition(0, lineSpacing * 2); + mEnableYumFinderBox.setPosition(0, lineSpacing * 1); + mEnableObjectSearchBox.setPosition(0, lineSpacing * 0); + mEnableFamilyDisplayBox.setPosition(0, lineSpacing * -1); + mEnableDangerousTileBox.setPosition(0, lineSpacing * -2); + mGenerateTownPlannerMapsBox.setPosition(0, lineSpacing * -3); + mEnableShowingHeldFoodPips.setPosition(0, lineSpacing * -4); + mEnableAlwaysShowPlayerLabelsBox.setPosition(0, lineSpacing * -5); mEnableFOVBox.setVisible( mPage == 0 ); mEnableCenterCameraBox.setVisible( mPage == 0 ); @@ -1476,6 +1502,7 @@ void SettingsPage::updatePage() { mEnableDangerousTileBox.setVisible(mPage == 5); mGenerateTownPlannerMapsBox.setVisible(mPage == 5); mEnableShowingHeldFoodPips.setVisible(mPage == 5); + mEnableAlwaysShowPlayerLabelsBox.setVisible(mPage == 5); mGameplayButton.setActive( mPage != 0 ); mControlButton.setActive( mPage != 1 ); diff --git a/gameSource/SettingsPage.h b/gameSource/SettingsPage.h index 53dab3837..de0ca9986 100644 --- a/gameSource/SettingsPage.h +++ b/gameSource/SettingsPage.h @@ -128,6 +128,7 @@ class SettingsPage : public GamePage, public ActionListener { CheckboxButton mEnableDangerousTileBox; CheckboxButton mGenerateTownPlannerMapsBox; CheckboxButton mEnableShowingHeldFoodPips; + CheckboxButton mEnableAlwaysShowPlayerLabelsBox; void checkRestartButtonVisibility(); diff --git a/gameSource/graphics/background.tga b/gameSource/graphics/background.tga index 80c1f616f..67c2eb66b 100644 Binary files a/gameSource/graphics/background.tga and b/gameSource/graphics/background.tga differ diff --git a/gameSource/graphicsSource/background.png b/gameSource/graphicsSource/background.png index fb263836b..13b15c56f 100644 Binary files a/gameSource/graphicsSource/background.png and b/gameSource/graphicsSource/background.png differ diff --git a/gameSource/minitech.cpp b/gameSource/minitech.cpp index ee417354f..9a414c5bc 100644 --- a/gameSource/minitech.cpp +++ b/gameSource/minitech.cpp @@ -1485,14 +1485,19 @@ void minitech::updateDrawTwoTech() { } if (trans->actor == -1 && trans->autoDecaySeconds != 0) { if ( trans->autoDecaySeconds < 0 ) { - drawObj(pos, trans->actor, "WAIT", to_string(- trans->autoDecaySeconds) + " HR"); + drawObj(pos, trans->actor, "WAIT", autoSprintf("%.0f HR", -trans->autoDecaySeconds)); } else { - int decayTime = trans->autoDecaySeconds; + float decayTime = trans->autoDecaySeconds; if (decayTime >= 60) { - decayTime = int(round(decayTime / 60.0)); - drawObj(pos, trans->actor, "WAIT", to_string(decayTime) + " MIN"); + if (int(decayTime) % 60 != 0) { + drawObj(pos, trans->actor, "WAIT", autoSprintf("%.1f MIN", decayTime / 60.0)); + } else { + drawObj(pos, trans->actor, "WAIT", autoSprintf("%.0f MIN", decayTime / 60.0)); + } + } else if (decayTime >= 1) { + drawObj(pos, trans->actor, "WAIT", autoSprintf("%.0f SEC", decayTime)); } else { - drawObj(pos, trans->actor, "WAIT", to_string(decayTime) + " SEC"); + drawObj(pos, trans->actor, "WAIT", autoSprintf("%.1f SEC", decayTime)); } } } else if (trans->actor == 0 && trans->contTransFlag != 0) { diff --git a/gameSource/objectBank.cpp b/gameSource/objectBank.cpp index bf0fec17e..74d2731c0 100644 --- a/gameSource/objectBank.cpp +++ b/gameSource/objectBank.cpp @@ -1070,6 +1070,72 @@ float initObjectBankStep() { next++; } + + + r->isTapOutTrigger = false; + + if( strstr( lines[next], "tapoutTrigger=" ) != NULL ) { + // tapoutTrigger flag present + + int tapoutTriggerRead = 0; + int value1 = -1; + int value2 = -1; + int value3 = -1; + int value4 = -1; + int value5 = -1; + int value6 = -1; + + int numRead = sscanf( lines[next], + "tapoutTrigger=%d#%d,%d,%d,%d,%d,%d", + &( tapoutTriggerRead ), + &value1, &value2, + &value3, &value4, + &value5, &value6 ); + + if( tapoutTriggerRead == 1 && + numRead >= 3 && numRead <= 7 ) { + // valid tapout trigger + TapoutRecord tr; + + tr.triggerID = r->id; + + tr.tapoutMode = value1; + tr.tapoutCountLimit = -1; + tr.specificX = 9999; + tr.specificY = 9999; + tr.radiusN = -1; + tr.radiusE = -1; + tr.radiusS = -1; + tr.radiusW = -1; + + if( tr.tapoutMode == 1 ) { + tr.specificX = value2; + tr.specificY = value3; + } + else if( tr.tapoutMode == 0 ) { + tr.radiusN = value3; + tr.radiusE = value2; + tr.radiusS = value3; + tr.radiusW = value2; + if( numRead == 5 ) + tr.tapoutCountLimit = value4; + } + else if( tr.tapoutMode == 2 ) { + tr.radiusN = value2; + tr.radiusE = value3; + tr.radiusS = value4; + tr.radiusW = value5; + if( numRead == 7 ) + tr.tapoutCountLimit = value6; + } + + tapoutRecords.push_back( tr ); + + r->isTapOutTrigger = tapoutTriggerRead; + } + + next++; + } @@ -2153,7 +2219,7 @@ void initObjectBankFinish() { for( int i=0; iisTapOutTrigger ) setupTapout( o ); } } @@ -2655,6 +2721,8 @@ int reAddObject( ObjectRecord *inObject, char *biomeString = getBiomesString( inObject ); + char *tapoutTriggerParameters = getTapoutTriggerString( inObject ); + int id = addObject( desc, inObject->containable, inObject->containSize, @@ -2683,6 +2751,8 @@ int reAddObject( ObjectRecord *inObject, inObject->race, inObject->deathMarker, inObject->homeMarker, + inObject->isTapOutTrigger, + tapoutTriggerParameters, inObject->floor, inObject->noCover, inObject->floorHugging, @@ -2740,6 +2810,7 @@ int reAddObject( ObjectRecord *inObject, inObject->cachedHeight ); delete [] biomeString; + delete [] tapoutTriggerParameters; return id; } @@ -3013,6 +3084,8 @@ int addObject( const char *inDescription, int inRace, char inDeathMarker, char inHomeMarker, + char inTapoutTrigger, + char *inTapoutTriggerParameters, char inFloor, char inPartialFloor, char inFloorHugging, @@ -3243,6 +3316,7 @@ int addObject( const char *inDescription, lines.push_back( autoSprintf( "male=%d", (int)inMale ) ); lines.push_back( autoSprintf( "deathMarker=%d", (int)inDeathMarker ) ); lines.push_back( autoSprintf( "homeMarker=%d", (int)inHomeMarker ) ); + if( inTapoutTrigger ) lines.push_back( autoSprintf( "tapoutTrigger=1#%s", inTapoutTriggerParameters ) ); lines.push_back( autoSprintf( "floor=%d", (int)inFloor ) ); if( inPartialFloor ) lines.push_back( autoSprintf( "partialFloor=%d", (int)inPartialFloor ) ); @@ -3598,6 +3672,7 @@ int addObject( const char *inDescription, r->homeMarker = inHomeMarker; + r->isTapOutTrigger = inTapoutTrigger; r->floor = inFloor; r->noCover = inPartialFloor; r->floorHugging = inFloorHugging; @@ -5806,6 +5881,37 @@ char *getBiomesString( ObjectRecord *inObject ) { return stringBuffer.getElementString(); } + +char *getTapoutTriggerString( ObjectRecord *inObject ) { + + char *working = NULL; + + TapoutRecord *tr = getTapoutRecord( inObject->id ); + + if( tr == NULL ) return NULL; + + if( tr->tapoutMode == 1 ) { + working = autoSprintf( "%d,%d,%d", tr->tapoutMode, tr->specificX, tr->specificY ); + } + else if( tr->tapoutMode == 0 ) { + if( tr->tapoutCountLimit == -1 ) { + working = autoSprintf( "%d,%d,%d", tr->tapoutMode, tr->radiusE, tr->radiusN ); + } + else { + working = autoSprintf( "%d,%d,%d,%d", tr->tapoutMode, tr->radiusE, tr->radiusN, tr->tapoutCountLimit ); + } + } + else if( tr->tapoutMode == 2 ) { + if( tr->tapoutCountLimit == -1 ) { + working = autoSprintf( "%d,%d,%d,%d,%d", tr->tapoutMode, tr->radiusN, tr->radiusE, tr->radiusS, tr->radiusW ); + } + else { + working = autoSprintf( "%d,%d,%d,%d,%d,%d", tr->tapoutMode, tr->radiusN, tr->radiusE, tr->radiusS, tr->radiusW, tr->tapoutCountLimit ); + } + } + + return working; + } diff --git a/gameSource/objectBank.h b/gameSource/objectBank.h index aadd20259..273c69b77 100644 --- a/gameSource/objectBank.h +++ b/gameSource/objectBank.h @@ -589,6 +589,8 @@ int addObject( const char *inDescription, int inRace, char inDeathMarker, char inHomeMarker, + char inTapoutTrigger, + char *inTapoutTriggerParameters, char inFloor, char inPartialFloor, char inFloorHugging, @@ -871,6 +873,8 @@ int getMouthIndex( ObjectRecord *inObject, double inAge ); char *getBiomesString( ObjectRecord *inObject ); +char *getTapoutTriggerString( ObjectRecord *inObject ); + void getAllBiomes( SimpleVector *inVectorToFill ); diff --git a/server/map.cpp b/server/map.cpp index 3596e4334..1be7a2e21 100644 --- a/server/map.cpp +++ b/server/map.cpp @@ -7095,132 +7095,239 @@ static void runTapoutOperation( int inX, int inY, int totalGridCells = 0; int currentGridCellIndex = -1; - // counting total cells in the grid - for( int y = inY - inRadiusS; - y <= inY + inRadiusN; - y ++ ) { - - for( int x = inX - inRadiusW; - x <= inX + inRadiusE; - x ++ ) { - - if( inX == x && inY == y ) { - // skip center - continue; - } + if( inR->tapoutMode == 0 && inR->tapoutCountLimit != -1 ) { - if( inR->tapoutMode == 2 && inX != x && inY != y ) { - // Directional tapout - // skip tiles not on the same x and y as trigger - continue; + // area tapout and there is a limit + // we will need to pick targets at random + + // counting total cells in the grid + + for( int y = inY - inRadiusS; + y <= inY + inRadiusN; + y ++ ) { + + for( int x = inX - inRadiusW; + x <= inX + inRadiusE; + x ++ ) { + + if( inX == x && inY == y ) { + // skip center + continue; + } + + int id = getMapObjectRaw( x, y ); + int id_floor = getMapFloor( x, y ); + TransRecord *t = getPTrans( inTriggerID, id ); + if( t != NULL && id == t->target ) totalGridCells++; + TransRecord *t_floor = getPTrans( inTriggerID, id_floor ); + if( t_floor != NULL && id_floor == t_floor->target ) totalGridCells++; } - - int id = getMapObjectRaw( x, y ); - int id_floor = getMapFloor( x, y ); - TransRecord *t = getPTrans( inTriggerID, id ); - if( t != NULL && id == t->target ) totalGridCells++; - TransRecord *t_floor = getPTrans( inTriggerID, id_floor ); - if( t_floor != NULL && id_floor == t_floor->target ) totalGridCells++; } } + if( inR->tapoutMode == 0 ) { + + // main loop for area tapout - for( int y = inY - inRadiusS; - y <= inY + inRadiusN; - y ++ ) { - - for( int x = inX - inRadiusW; - x <= inX + inRadiusE; - x ++ ) { - - if( inX == x && inY == y ) { - // skip center - continue; - } + for( int y = inY - inRadiusS; + y <= inY + inRadiusN; + y ++ ) { + + for( int x = inX - inRadiusW; + x <= inX + inRadiusE; + x ++ ) { + + if( inX == x && inY == y ) { + // skip center + continue; + } - if( inR->tapoutMode == 2 && inX != x && inY != y ) { - // Directional tapout - // skip tiles not on the same x and y as trigger - continue; - } + for( int p=0; p<2; p++ ) { - for( int p=0; p<2; p++ ) { + // tapout object in first pass + // tapout floor in second pass - // tapout object in first pass - // tapout floor in second pass + int id; + if( p == 0 ) { + id = getMapObjectRaw( x, y ); + } + else if( p == 1 ) { + id = getMapFloor( x, y ); + } + + // change triggered by tapout represented by + // tapoutTrigger object getting used as actor + // on tapoutTarget + TransRecord *t = getPTrans( inTriggerID, id ); - int id; - if( p == 0 ) { - id = getMapObjectRaw( x, y ); - } - else if( p == 1 ) { - id = getMapFloor( x, y ); - } - - // change triggered by tapout represented by - // tapoutTrigger object getting used as actor - // on tapoutTarget - TransRecord *t = getPTrans( inTriggerID, id ); + if( t != NULL ) { - if( t != NULL ) { + currentGridCellIndex++; + + if( inR->tapoutCountLimit != -1 ) { + // this turns the loop into a totalGridCells draws inR->tapoutCountLimit + double P = (double)(inR->tapoutCountLimit - tapoutCount) / (totalGridCells - currentGridCellIndex); + + double p = randSource.getRandomBoundedDouble( 0, 1 ); + + if( p >= P ) continue; + } - currentGridCellIndex++; - - if( inR->tapoutCountLimit != -1 ) { - // this turns the loop into a totalGridCells draws inR->tapoutCountLimit - double P = (double)(inR->tapoutCountLimit - tapoutCount) / (totalGridCells - currentGridCellIndex); + tapoutCount++; + + if( p == 0 ) { + setMapObjectRaw( x, y, t->newTarget ); + } + else if( p == 1 ) { + setMapFloor( x, y, t->newTarget ); + } + + TransRecord *newDecayT = getMetaTrans( -1, t->newTarget ); + + timeSec_t mapETA = 0; + + if( newDecayT != NULL ) { + + // add some random variation to avoid lock-step + // especially after a server restart + double tweakedSeconds = + randSource.getRandomBoundedDouble( + newDecayT->autoDecaySeconds * 0.9, + newDecayT->autoDecaySeconds ); - double p = randSource.getRandomBoundedDouble( 0, 1 ); + mapETA = MAP_TIMESEC + tweakedSeconds; + } + else { + // no further decay + mapETA = 0; + } + + if( p == 0 ) { + setEtaDecay( x, y, mapETA, newDecayT ); + } + else if( p == 1 ) { + setFloorEtaDecay( x, y, mapETA, newDecayT ); + } - if( p >= P ) continue; + setEtaDecay( x, y, mapETA, newDecayT ); + + } + + if( inR->tapoutCountLimit != -1 && tapoutCount >= inR->tapoutCountLimit ) { + return; } - tapoutCount++; + } + } + } + } + else if( inR->tapoutMode == 2 ) { + + // directional tapout + + // we tapout from inward to out; and N, E, S, W, in that order + // if there is a limit, tapout the closest ones (instead of random like area tapout) + + int i = 1; // skip center + int maxRadius = 0; + if( maxRadius < inR->radiusN ) maxRadius = inR->radiusN; + if( maxRadius < inR->radiusE ) maxRadius = inR->radiusE; + if( maxRadius < inR->radiusS ) maxRadius = inR->radiusS; + if( maxRadius < inR->radiusW ) maxRadius = inR->radiusW; + + while( i <= maxRadius ) { + + for( int d = 0; d < 4; d++ ) { + + int x, y; + if( d == 0 ) { + if( i > inR->radiusN ) continue; + x = inX+0; y = inY+i; + } + else if( d == 1 ) { + if( i > inR->radiusE ) continue; + x = inX+i; y = inY+0; + } + else if( d == 2 ) { + if( i > inR->radiusS ) continue; + x = inX+0; y = inY-i; + } + else if( d == 3 ) { + if( i > inR->radiusW ) continue; + x = inX-i; y = inY+0; + } + + for( int p=0; p<2; p++ ) { + + // tapout object in first pass + // tapout floor in second pass + + int id; if( p == 0 ) { - setMapObjectRaw( x, y, t->newTarget ); + id = getMapObjectRaw( x, y ); } else if( p == 1 ) { - setMapFloor( x, y, t->newTarget ); - } - - TransRecord *newDecayT = getMetaTrans( -1, t->newTarget ); - - timeSec_t mapETA = 0; - - if( newDecayT != NULL ) { - - // add some random variation to avoid lock-step - // especially after a server restart - double tweakedSeconds = - randSource.getRandomBoundedDouble( - newDecayT->autoDecaySeconds * 0.9, - newDecayT->autoDecaySeconds ); - - mapETA = MAP_TIMESEC + tweakedSeconds; + id = getMapFloor( x, y ); } - else { - // no further decay - mapETA = 0; - } - - if( p == 0 ) { + + // change triggered by tapout represented by + // tapoutTrigger object getting used as actor + // on tapoutTarget + TransRecord *t = getPTrans( inTriggerID, id ); + + if( t != NULL ) { + + tapoutCount++; + + if( p == 0 ) { + setMapObjectRaw( x, y, t->newTarget ); + } + else if( p == 1 ) { + setMapFloor( x, y, t->newTarget ); + } + + TransRecord *newDecayT = getMetaTrans( -1, t->newTarget ); + + timeSec_t mapETA = 0; + + if( newDecayT != NULL ) { + + // add some random variation to avoid lock-step + // especially after a server restart + double tweakedSeconds = + randSource.getRandomBoundedDouble( + newDecayT->autoDecaySeconds * 0.9, + newDecayT->autoDecaySeconds ); + + mapETA = MAP_TIMESEC + tweakedSeconds; + } + else { + // no further decay + mapETA = 0; + } + + if( p == 0 ) { + setEtaDecay( x, y, mapETA, newDecayT ); + } + else if( p == 1 ) { + setFloorEtaDecay( x, y, mapETA, newDecayT ); + } + setEtaDecay( x, y, mapETA, newDecayT ); + } - else if( p == 1 ) { - setFloorEtaDecay( x, y, mapETA, newDecayT ); - } - - setEtaDecay( x, y, mapETA, newDecayT ); - } + if( inR->tapoutCountLimit != -1 && tapoutCount >= inR->tapoutCountLimit ) { + return; + } - if( inR->tapoutCountLimit != -1 && tapoutCount >= inR->tapoutCountLimit ) { - return; } } - + + i++; + } } diff --git a/server/server.cpp b/server/server.cpp index ae57c9d94..363410f9d 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -9704,7 +9704,7 @@ char removeFromContainerToHold( LiveObject *inPlayer, // Execute containment transitions - removeFromContainerToHold - container - if( contTrans != NULL ) { + if( contTrans != NULL && target != contTrans->newActor ) { setMapObject( inContX, inContY, contTrans->newActor ); } @@ -18554,6 +18554,73 @@ int main() { } } } + else { + // target location emtpy + // and we're not holding anything + // check bare-hand transition on floor + + int floorID = getMapFloor( m.x, m.y ); + + if( floorID > 0 ) { + + TransRecord *r = + getPTrans( 0, + floorID ); + + + if( r != NULL && + // make sure we're not too young + // to hold result of on-floor + // transition + ( r->newActor == 0 || + getObject( r->newActor )-> + minPickupAge <= + computeAge( nextPlayer ) ) ) { + + // applies to floor + int resultID = r->newTarget; + + if( getObject( resultID )->floor ) { + // changing floor to floor + // go ahead + + if( resultID != floorID ) { + setMapFloor( m.x, m.y, + resultID ); + } + handleHoldingChange( nextPlayer, + r->newActor ); + + setHeldGraveOrigin( nextPlayer, + m.x, m.y, + resultID ); + } + else { + // changing floor to non-floor + char canPlace = true; + if( getObject( resultID )-> + blocksWalking && + ! isMapSpotEmpty( m.x, m.y ) ) { + canPlace = false; + } + + if( canPlace ) { + setMapFloor( m.x, m.y, 0 ); + + setMapObject( m.x, m.y, + resultID ); + + handleHoldingChange( + nextPlayer, + r->newActor ); + setHeldGraveOrigin( nextPlayer, + m.x, m.y, + resultID ); + } + } + } + } + } if( target == 0 && newGroundObject > 0 ) {