Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Villager v2 and trade functionality #6310

Open
wants to merge 30 commits into
base: minor-next
Choose a base branch
from

Conversation

alvin0319
Copy link
Contributor

@alvin0319 alvin0319 commented Mar 26, 2024

Introduction

This PR implements Villager V2 and trade functionality.

Relevant issues

Changes

API changes

  • Added VillagerV2:
    • Villager V2 has 15 variants: Unemployed, Farmer, Fisherman, Shepherd, Fletcher, Librarian, Cartographer, Cleric, Armorer, Weaponsmith, Toolsmith, Butcher, Leatherworker, Mason (aka Stone Mason), Nitwit
    • Unemployed and Nitwit villager can't trade, so they don't have recipes.
    • Villagers now have their recipes corresponding their professions (except unimplemented items), following this wiki.
    • VillagerV2::setProfession(VillagerProfession): Sets a profession of this villager, also updates recipes.
    • VillagerV2::getProfession() : VillagerProfession: Returns the current profession of the villager.
    • VillagerV2::getRecipeData() : RecipeData: Returns the recipe data of this villager.
  • Added VillagerProfession and its related friends:
    • Each professions have their corresponding translation key (for their name), variant id and job block.
  • Added TradeRecipeData:
    • TradeRecipeData contains necessary values for trade: recipes, current tier, trader experience, tier experience requirements
  • Added TradeRecipe:
    • buyA: Item that trader wants.
    • sell: Item that trader gives to you.
    • buyB: Item that trader additionally wants, optional.
    • maxUses: Max uses of this trade. 16 is default.
    • priceMultiplier: affects price of item, not used currently. 0.05 is default.
    • rewardExp: Reward exp that player earns by completing this trade. 0 is default.
    • tier: Required tier for this trade. 0 is default.
    • traderExp: Reward exp for the villager. 0 is default.
    • uses: Amount of current uses of this recipe. 0 is default.

Behavioural changes

Villager Spawn Egg will now spawn Villager V2 instead of legacy Villager.

Backwards compatibility

There is no known BC breaking change.

Follow-up

N/A

Tests

I tested this PR by doing the following (tick all that apply):

  • Writing PHPUnit tests (commit these in the tests/phpunit folder)
  • Playtesting using a Minecraft client (provide screenshots or a video)
  • Writing a test plugin (provide the code and sample output)
  • Other (provide details)
Minecraft.2024-03-26.14-47-44.mp4

Plugin used for this test:

<?php

/**
 * @name TradeTest
 * @author alvin0319
 * @main alvin0319\TradeTest\TradeTest
 * @version 1.0.0
 * @api 5.0.0
 */

declare(strict_types=1);

namespace alvin0319\TradeTest;

use pocketmine\entity\Location;
use pocketmine\entity\trade\TradeRecipe;
use pocketmine\entity\trade\TradeRecipeData;
use pocketmine\entity\Villager;
use pocketmine\event\EventPriority;
use pocketmine\event\player\PlayerJoinEvent;
use pocketmine\inventory\TradeInventory;
use pocketmine\item\VanillaItems;
use pocketmine\player\Player;
use pocketmine\plugin\PluginBase;

final class TradeTest extends PluginBase{

	protected function onEnable() : void{
		$this->getServer()->getPluginManager()->registerEvent(PlayerJoinEvent::class, fn(PlayerJoinEvent $event) => $this->sendTrade($event->getPlayer()), EventPriority::NORMAL, $this);
	}

	/**
	 * This trade UI implements some offers of Fisherman in Bedrock Edition
	 * @see https://minecraft.wiki/w/Trading#Fisherman_2
	 */
	private function sendTrade(Player $player) : void{
		// spawn villager entity 1.5 block above the player
		$villager = new Villager(
			Location::fromObject(
				$player->getDirectionVector()->multiply(1.5)->addVector($player->getPosition()),
				$player->getWorld()
			)
		);
		$villager->spawnToAll();
		$inventory = new TradeInventory("Trade Test", $villager, new TradeRecipeData([
			new TradeRecipe(
				buyA: VanillaItems::STRING()->setCount(20),
				sell: VanillaItems::EMERALD()->setCount(1),
				maxUses: 16,
				tier: 0,
				traderExp: 2
			),
			new TradeRecipe(
				buyA: VanillaItems::EMERALD()->setCount(3),
				sell: VanillaItems::COOKED_FISH()->setCount(1),
				buyB: VanillaItems::RAW_FISH()->setCount(6),
				maxUses: 16,
				tier: 0,
				traderExp: 1
			),
			new TradeRecipe(
				buyA: VanillaItems::RAW_FISH()->setCount(15),
				sell: VanillaItems::EMERALD()->setCount(1),
				maxUses: 16,
				tier: 1,
				traderExp: 10
			),
			new TradeRecipe(
				buyA: VanillaItems::EMERALD()->setCount(1),
				sell: VanillaItems::COOKED_SALMON()->setCount(6),
				buyB: VanillaItems::RAW_SALMON()->setCount(6),
				maxUses: 16,
				tier: 1,
				traderExp: 5
			),
		], 1));
		$player->setCurrentWindow($inventory);
	}
}

@ShockedPlot7560
Copy link
Member

Do we really want to force an API to everyone ? I can only think about the Form API. Wouldn't it be the function of a virion to do so?

@Matze997
Copy link

On the other hand people will know as long as pocketmine gets updated this feature will be supported

@alvin0319
Copy link
Contributor Author

Do we really want to force an API to everyone ? I can only think about the Form API. Wouldn't it be the function of a virion to do so?

I don't think it is possible to implement this with virion so.

@alvin0319 alvin0319 changed the title Implement villager trade API Implement Villager v2 and trade functionality Apr 1, 2024
@alvin0319 alvin0319 marked this pull request as ready for review April 1, 2024 14:40
@alvin0319
Copy link
Contributor Author

Now ready to be reviewed.

@IvanCraft623 IvanCraft623 added the Category: API Related to the plugin API label Apr 1, 2024
@IvanCraft623 IvanCraft623 added Category: Gameplay Related to Minecraft gameplay experience Category: Core Related to internal functionality Requires translations Type: Enhancement Contributes features or other improvements to PocketMine-MP labels Apr 1, 2024
Copy link
Member

@IvanCraft623 IvanCraft623 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick review, I still have to validate with BDS behavior

src/entity/VillagerV2.php Outdated Show resolved Hide resolved
src/entity/VillagerV2.php Outdated Show resolved Hide resolved
src/entity/VillagerV2.php Outdated Show resolved Hide resolved
final class ArmorerProfession extends VillagerProfession{

public function __construct(){
parent::__construct(VillagerProfessionTypeIds::ARMORER, "entity.villager.armor", VanillaBlocks::BLAST_FURNACE());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a translation key, make a PR in https://github.com/pmmp/Language to later use a constant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a client-side translation key, so I don't think PM-side translations are needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid hardcode translations, this is not the only place where translation is only used client-side, e.g. names of enchantments.

}

/** @phpstan-return list<TradeRecipe> */
public function getRecipes(int $biomeId) : array{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In vanilla only a few recipes are selected per group, see https://github.com/Mojang/bedrock-samples/blob/main/behavior_pack/trading/armorer_trades.json, for more details. Probably with a function that generates recipes is not enough and will have to be extended to a dedicated class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So as far as I understand, (also according to bedrock.dev wiki), recipes are separated something called "groups" and they are randomly selected whenever trade recipes are generated, right? Perhaps we could make a script that generates recipe class from behaviour pack.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be best if this is self-generated. I think that for now we only need to make an api to implement trade tables with their groups and leave the generation of recipes for another PR.

src/entity/trade/TradeRecipe.php Outdated Show resolved Hide resolved

namespace pocketmine\entity\trade;

final class TradeRecipeData{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the name of the class, it gives me to understand that it contains the data of a single recipe, or that it's for data processing, maybe something like TradeRecipeManager, TradeRecipeGroup, or TradeRecipesHolder would be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not sure about this class name. TradeRecipeManager feels like it's too generic (which people might understand it manages all trade recipes), TradeRecipeGroup doesn't seem valid since it doesn't only contain trade recipes. TradeRecipesHolder might be good but I think we still need better name than these. (or separate other values like tier, exp to other class?)

src/event/player/PlayerItemTradeEvent.php Show resolved Hide resolved
src/event/player/PlayerItemTradeEvent.php Show resolved Hide resolved
use pocketmine\entity\Living;
use pocketmine\network\mcpe\protocol\ClientboundPacket;

abstract class EntityInventory extends SimpleInventory{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What other uses would this class have?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if there are any other use cases (including future), as far as I know trade is the only one which doesn't use ContainerOpen for opening inventory.

@IvanCraft623
Copy link
Member

The pull request is very bloated by the recipes generation, so perhaps it would be nice to leave them for a separate PR

@ShockedPlot7560
Copy link
Member

At the moment I'm still dubious about the fact that the API has been implemented right down to the NPC itself.

Is this a mission of pocketmine itself, or in the same way as the forms, we should let the community create libraries instead of imposing one.

@alvin0319
Copy link
Contributor Author

It isn't same with forms. Trade needs a lot of reflection hacks and closure hacks to work on plugins.

@pandaa-be
Copy link
Contributor

At the moment I'm still dubious about the fact that the API has been implemented right down to the NPC itself.

Is this a mission of pocketmine itself, or in the same way as the forms, we should let the community create libraries instead of imposing one.

by the way, villager trading is not only an api but also implements the normal trades now, unlike before.

i find it nice that the pr also offer an api to be able to make trades, if there were several different plugins the support on the discord server would be annoying just like with scoreboard, bossbar and forms apis - it's annoying if someone uses jojo's api but you don't know the api yourself it would be better if there was one in the core that everyone uses

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Category: API Related to the plugin API Category: Core Related to internal functionality Category: Gameplay Related to Minecraft gameplay experience Requires translations Type: Enhancement Contributes features or other improvements to PocketMine-MP
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants