One of the most common misunderstandings of TCP vs UDP is that TCP's main feature is reliability.
The main feature of TCP is that it abstracts a stream of data to send from one socket to another. Reliability, as provided by TCP is a requirement for the abstraction to work, but is not the central idea behind TCP. You might want to consider basing your decisions on whether or not a stream abstraction is appropriate for your needs.
A stream abstraction means that you have a place where you put bytes, and the same bytes appear on the other end as soon as possible. That's all there is to it.
TCP has no concept of messages. As so, it is perfectly possible that you make two sends of 10 bytes each, and you get one receive of 20 bytes. Also it is possible that you get 3 receives, one of 1 byte, one of 14 bytes and one of 5 bytes. If you need to have messages on TCP, you will have to implement your own messaging abstraction over TCP.
Additionally, TCP makes no guarantees as of when the messages will arrive on the other side. It is perfectly possible for you to do one million minus one 10-byte sends, wait 1 hour and then do one final 10 byte send, and only get one 10 megabyte receive at the other end.
There are ways to get around some of these limitations, but the main idea is that TCP abstracts a stream of bytes, and should be used when you need a stream of bytes that you want to send as soon as possible to another place. File sends are an awesome example of an application that makes good use of TCP.
On the other hand, UDP offers you a messaging abstraction. You send messages, and they get immediately sent to the other side. Since messages are completely independent of each other, there is no concept of order, and as such it is perfectly possible for sent messages to arrive in disorder, or not arrive at all. If you want order, and delivery guarantees, you will have to implement those on top of UDP.
However, unlike TCP, each message on UDP is a separate entity. Each send will result in one receive at the other side (supposing the message did not get lost). Messages won't be buffered, split or reassembled anywhere. Also, UDP guarantees that each message will be sent as soon as possible.
For games, you may want to think which abstraction works best for you (stream or messages), and build your communications infrastructure on top of that. If there are additional requirements that the abstraction does not offer, you can build on top of them according to your needs.
For action-based, I would most likely use UDP, as each action can probably be converted into one message. Since UDP does not have any concept of order and reliability, I would insert a few bytes on top of each message, which state a sequence number for that message, and the last sequence number I have received from the other end.
If both hosts are constantly exchanging messages, a host that notices that the most recent messages have not been received by the other host, can assume that a particular message was lost, and could try retransmitting it. This is a very simple order/reliability system that can be easily implemented on top of UDP.
There are many other fun things you can do with UDP, like creating separate channels, each with different sequence numbers, so a message lost on one channel won't affect the rest, or a specific channel without sequence numbers.
Unfortunately, I find it way too common for people to stop at "TCP:Reliable:GOOD!, UDP:Unreliable:BAD!", without considering the more important difference between the protocols.