As @kindofdev points out, it is very simple to burn tokens with Plutus scripts. It is a little more involved if you want some kinda of side-effect/record of burning tokens. Still shouldn't be too difficult, although Plutus takes some time to learn.
There are no bounds on what can be sent to a Plutus Script, i.e. the script doesn't do any checks on new UTxOs at the address. This means you can't make the script do something just by sending something to it. The script only sees what is included in transactions spending the UTxOs at that address or minted from the address. It can see the entire transaction though, so multiple scripts can ensure something happens in other scripts atomically before validating.
Why does that matter?
You mention that you want to, for example, send an NFT to be replaced with an "evolved" NFT. You can definitely do this. In the simplest case, you can have your minting policy check that the old NFT is being burnt, at which point it will allow minting an "evolved" NFT.
Minting policies can't track any state though. They don't have a datum. So if you want to include any additional context for when tokens can be minted or evolved, that state must be stored on a separate validator script. This will require (at the very least) one validator script and one minting policy:
- A script for validating the burning of the original NFT
- A minting policy for minting the new "evolved" NFT
A transaction for evolving your NFT would spend a datum at Script #1 and replace it with a new datum (either identical or with some book-keeping data). The transaction would also mint the new NFT with Minting Policy #2.
The minting policy will only validate if the Script #1 Datum is spent in the transaction and the script will only validate if the old NFT is spent to the script and the new NFT is minted + any additional constraints you want to include.
You can have your "evolve" minting policy check if the pre-evolution NFT is being burned. If you need to include additional state/context for when that can happen, you can include that data on a separate, but inter-dependent, validator script.
Hope that helps. Might be worth looking at these other questions to give you more ideas:
How to lock funds in a smart contract?
Ensuring NFT collection uniqueness using counter as asset name?
Can a smart contract only be interacted with once