The Perfect Murder

Individual Project
Spring 2018
**Planning to update the code, and fix bugs and such on Summer 2020.

The Perfect Murder is a Text Adventure game made for one of my classes at Montgomery College. Players play as a detective trying to investigative what seemed to be a “suicide”. They will have to find clues to see what truly happened but in the end, players will need to see if their odds are in their favor or not to find the real culprit.

The things I worked on in this project were:

– Game Design
– Programming
– Debugging
– Design Documentation
– UI (HUD)
– Character Dialogue
– Sprite finder

I took inspiration from the Ace Attorney series to create this game. Some of the things I wanted to accomplish was being able to display the character’s inventory/clues as they picked them up. Also, give the feel that the player could explore “freely” and get engaged in the story.

SCREENSHOTS

Some Code
(planning to be updated and fixed on Summer 2020)

Start Scene Code

public class GameLevel : MonoBehaviour
{
    public Text gameText;
    public bool isImgOn;
    public Image img;
    public enum playersStage { look_phone, phone_rings, answer_phone, ignore_phone, go_case, ignore_case };
    public playersStage currentStage;

    // Use this for initialization
    void Start()
    {
        img.enabled = true;
        isImgOn = true;
    }

    // Update is called once per frame
    void Update()
    {
        if (currentStage == playersStage.look_phone) { LookPhone(); }
        else if (currentStage == playersStage.phone_rings) { PhoneRings(); }
        else if (currentStage == playersStage.answer_phone) { AnswerPhone(); }
        else if (currentStage == playersStage.ignore_phone) { IgnorePhone(); }
        else if (currentStage == playersStage.go_case) { GoCase(); }
        else if (currentStage == playersStage.ignore_case) { IgnoreCase(); }

        if (currentStage != playersStage.look_phone || currentStage != playersStage.go_case)
        {

            if (isImgOn == true)
            {

                img.enabled = false;
                isImgOn = false;
            }
          
        }
    }

    void LookPhone()
    {
        gameText.text = "Finally, I have a little time for myself. I wonder if I can play a couple of Clash Royale matches.\n\n" +
            "Press <color=#ff0000ff>\"P\"</color> to pick-up your phone.";
        if (Input.GetKeyDown(KeyCode.P)) { currentStage = playersStage.phone_rings; }
    }

    void PhoneRings()
    {
        gameText.text = "Hmmm... An incoming call? From the police department? \n" + "-sigh-\n" + "I doubt they have something good to tell me.\n\n" + "Press <color=#ff0000ff>\"A\"</color> to answer the call.\n"
            + "Press <color=#ff0000ff>\"D\"</color> to decline it.";

        if (Input.GetKeyDown(KeyCode.A)) { currentStage = playersStage.answer_phone; }
        if (Input.GetKeyDown(KeyCode.D)) { currentStage = playersStage.ignore_phone; }


    }

    void AnswerPhone()
    {
        gameText.text = "-A lot of talking goes on-\n" + "Uh uh, yeah. I see...\n" + "<color=#FF00FF><i>Yeah sir, the suicide took place in your neighborhood and the victim's name is Ainhoa.</i></color>\n" + "What?! " +
            "This can't be... She was the happiest teenager in the neighborhood... How this happened? This is strange.\n\n" + "Press <color=#ff0000ff>\"T\"</color> to take the case and go for clues.\n"
            + "Press <color=#ff0000ff>\"N\"</color> to not go.";
        if (Input.GetKeyDown(KeyCode.T)) { currentStage = playersStage.go_case; }
        if (Input.GetKeyDown(KeyCode.N)) { currentStage = playersStage.ignore_case; }
    }

    void IgnorePhone()
    {
        gameText.text = "-Phone keeps ringing incessantly-\n" + "Dang this technology. I'll pick up, stop ringing!\n\n" + "Press <color=#ff0000ff>\"A\"</color> to finally answer.";
        if (Input.GetKeyDown(KeyCode.A)) { currentStage = playersStage.answer_phone; }
    }

    void GoCase()
    {
        gameText.text = "This has to be a mistake. I'm not the best at spotting depression, but that girl had anything but that.\n" + "Something is wrong here.\n" + "-You take a cup of coffee " +
            "in a dramatic way, while you prepare yourself to get out-\n\n" + "Press <color=#ff0000ff>\"G\"</color> to go to your neighborhood.";
        if (Input.GetKeyDown(KeyCode.G)) { SceneManager.LoadScene("Neighborhood"); }
    }

    void IgnoreCase()
    {
        gameText.text = "Meh, maybe she listened to too much emo music.\n" + "Well, I think I'll keep playing Crash Royale.\n\n" +
            "Press <color=#ff0000ff>\"K\"</color> to keep playing in your phone.";
        if (Input.GetKeyDown(KeyCode.K)) { SceneManager.LoadScene("Lose"); }
    }

}

Neighborhood Scene Code

public class NeighborhoodScript : MonoBehaviour
{

    public Text gameText;
    public bool isImgOn;
    public bool talkingMom;
    public bool talkingForensic;
    public Image imgMom;
    public Image imgForensic;
    public Image imgKey;
    public Image imgNotes;
    public Image imgRazorBlade;
    public Image imgPictureTeeth;
    public Image imgSuicideNote;
    public enum playersStage { standing, talk_mom, talk_forensic };
    public playersStage currentStage;

    void Start()
    {
        imgMom.enabled = true;
        imgForensic.enabled = true;
        imgKey.enabled = intToBool(PlayerPrefs.GetInt("keyStatus"));
        imgNotes.enabled = intToBool(PlayerPrefs.GetInt("notesStatus"));
        imgRazorBlade.enabled = intToBool(PlayerPrefs.GetInt("bladeStatus"));
        imgPictureTeeth.enabled = intToBool(PlayerPrefs.GetInt("pictureStatus"));
        imgSuicideNote.enabled = intToBool(PlayerPrefs.GetInt("suicideStatus"));
        isImgOn = false;
    }

    void Update()
    {
        if (currentStage == playersStage.standing) { Standing(); }
        else if (currentStage == playersStage.talk_mom && !talkingMom) { TalkMom(); }
        else if (currentStage == playersStage.talk_forensic && !talkingForensic) { TalkForensic(); }
        CheckInputs();
    }

    void Standing()
    {
        gameText.text = "There is too many people from the neighborhood trying to figure it out what is happening. Everything seems tense...\n\n" +
            "Press <color=#ff0000ff>\"M\"</color> to talk with the victim's mom.\n" + "Press <color=#ff0000ff>\"F\"</color> to talk with the forensic.\n" +
            "Press <color=#ff0000ff>\"C\"</color> to go to the crime scene.\n";
    }

    void TalkMom()
    {
        imgForensic.enabled = false;
        talkingMom = true;
        if (imgKey.enabled == false)
        {
            PlayerPrefs.SetInt("keyStatus", 1);
            imgKey.enabled = intToBool(PlayerPrefs.GetInt("keyStatus"));
            gameText.text = "<color=#FF00FF><i>My daughter was the happiest person ever... She would never do this, not like this. Not without signs of suicidal thoughts.</i></color>\n" +
                "<color=#FF00FF><i>Please detective. I know there is something wrong. Take the key from Ainhoa's room and see if you can find anything.</i></color>\n" +
                "<i>A key was added to your inventory</i>\n" +
                "-Ainhoa's mother starts sobbing while you rest your hand in her should trying to comfort her-\n\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return.";
        }
        else if (imgKey.enabled == true)
        {
            gameText.text = "<color=#FF00FF><i>Please, make justice for my daughter.</i></color>\n" +
                "-Ainhoa's mother starts sobbing while you rest your hand in her should trying to comfort her-\n\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return.";    
        }
    }

    void TalkForensic()
    {
        imgMom.enabled = false;
        talkingForensic = true;
        if (imgNotes.enabled == false)
        {
            PlayerPrefs.SetInt("notesStatus", 1);
            imgNotes.enabled = intToBool(PlayerPrefs.GetInt("notesStatus"));
            isImgOn = true;
            gameText.text = "-The forensic starts to explain boring stuff-\n" + 
                "<color=#FF00FF><i>... Well, it looks like a suicide. I'll give you the autopsy report if you want to look at it.</i></color>\n" +
                "<i>The autopsy report was added to your inventory.</i>\n" +
                "I still can't understand why she did it...\n\n" + 
                "Press <color=#ff0000ff>\"R\"</color> to return.";
        }
        else if (imgNotes.enabled == true)
        {
            gameText.text = "<color=#FF00FF><i>I have nothing else to report.</i></color>\n" + 
                "-The forensic takes a cigarrete and starts smoking-\n" +
                "How ironic...\n\n" + "Press <color=#ff0000ff>\"R\"</color> to return.";
        }

    }

        void CheckInputs()
        {
            switch (currentStage)
            {
                case playersStage.standing:
                    if (Input.GetKeyDown(KeyCode.M)) { currentStage = playersStage.talk_mom; }
                    if (Input.GetKeyDown(KeyCode.F)) { currentStage = playersStage.talk_forensic; }
                    if (Input.GetKeyDown(KeyCode.C)) { SceneManager.LoadScene("CrimeScene"); }
                    break;
                case playersStage.talk_mom:
                    if (Input.GetKeyDown(KeyCode.R))
                    {
                        currentStage = playersStage.standing;
                        talkingMom = false;
                        imgForensic.enabled = true;
                    }
                    break;
                case playersStage.talk_forensic:
                    if (Input.GetKeyDown(KeyCode.R))
                    {
                        currentStage = playersStage.standing;
                        talkingForensic = false;
                        imgMom.enabled = true;
                    }
                    break;
            }
        }

    bool intToBool(int val)
    {
        if (val != 0)
            return true;
        else
            return false;
    }
}

Crime Scene Code

public class CrimeScene : MonoBehaviour
{

    public Text gameText;
    public bool isImgOn;
    public bool handBruise;
    public bool brokenTeeth;
    public bool autopsyReport;
    public Image imgKey;
    public Image imgNotes;
    public Image imgRazorBlade;
    public Image imgPictureTeeth;
    public Image imgSuicideNote;
    public Image imgAutopsy;
    public enum playersStage { body, inspect_body, inspect_hand, inspect_head, inspect_mouth, read_autopsy, cant_enter };
    public playersStage currentStage;

    void Start()
    {
        imgAutopsy.enabled = false;
        imgKey.enabled = intToBool(PlayerPrefs.GetInt("keyStatus"));
        imgNotes.enabled = intToBool(PlayerPrefs.GetInt("notesStatus")) ;
        imgRazorBlade.enabled = intToBool(PlayerPrefs.GetInt("bladeStatus"));
        imgPictureTeeth.enabled = intToBool(PlayerPrefs.GetInt("pictureStatus"));
        imgSuicideNote.enabled = intToBool(PlayerPrefs.GetInt("suicideStatus"));
        
        isImgOn = false;
    }

    void Update()
    {
        if (currentStage == playersStage.body) { Body(); }
        else if (currentStage == playersStage.inspect_body) { InspectBody(); }
        else if (currentStage == playersStage.inspect_hand && !handBruise) { InspectHand(); }
        else if (currentStage == playersStage.inspect_head) { InspectHead(); }
        else if (currentStage == playersStage.inspect_mouth && !brokenTeeth) { InspectMouth(); }
        else if (currentStage == playersStage.read_autopsy) { ReadAutopsy(); }
        else if (currentStage == playersStage.cant_enter) { CantEnter(); }

        CheckInputs();
    }

    void Body()
    {
            gameText.text = "-sigh-\n" + "Poor girl... What was she thinking?\n\n" + 
                "Press <color=#ff0000ff>\"V\"</color> to inspect the victim's body.\n" +
                "Press <color=#ff0000ff>\"B\"</color> to check the victim's room for clues.\n" + 
                "Press <color=#ff0000ff>\"K\"</color> to check the kitchen for clues.\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return to the front of the house.\n" + "";
     
    }

    void InspectBody()
    {
            gameText.text = "-The detective puts a pair of gloves on-\n" + "Time to get some clues.\n\n" +
                "Press <color=#ff0000ff>\"A\"</color> to read the autopsy.\n" +
                "Press <color=#ff0000ff>\"H\"</color> to inspect the victim's hand.\n" +
                "Press <color=#ff0000ff>\"C\"</color> to inspect the victim's head.\n" +
                "Press <color=#ff0000ff>\"M\"</color> to inspect the victim's mouth.\n" + 
                "Press <color=#ff0000ff>\"R\"</color> to return.\n";
    }

    void InspectHand()
    {
        
        handBruise = true;
        if (imgRazorBlade.enabled == false)
        {
            PlayerPrefs.SetInt("bladeStatus", 1);
            imgRazorBlade.enabled = intToBool(PlayerPrefs.GetInt("bladeStatus"));
            isImgOn = true;
            gameText.text = "The victim tried to make sure to have a successful \"suicide\" by cutting her wrists. However, the way her bruises were made makes me think that she didn't do it herself.\n" +
                "-The detective finds a razor blade-\n" +
                "<i>A razor blade was added to your inventory</i>\n" +
                "Her mother might be right, maybe she didn't commit suicide...\n\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return.";
        }
        else if (imgRazorBlade.enabled == true)
        {
            gameText.text = "There are no more clues here.\n\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return.";       
        }
    }

    void InspectHead()
    {
        gameText.text = "No head trauma, no blood, no anormalities. There is not much to see here.\n\n" + 
            "Press <color=#ff0000ff>\"R\"</color> to return.";
    }

    void InspectMouth()
    {
        
        brokenTeeth = true;
        if (imgPictureTeeth.enabled == false)
        {
            PlayerPrefs.SetInt("pictureStatus", 1);
            imgPictureTeeth.enabled = intToBool(PlayerPrefs.GetInt("pictureStatus"));
            isImgOn = true;
            gameText.text = "By the way her lips are, it is obvious that she drink (or someone made her drink) poison.\n" + 
                "-checks her mouth-\n" + 
                "There is a broken teeth. From her pictures, her teeths were ok before the suicide. Why would they be like that? I have to take a picture of this\n" +
                "-The detective takes a picture of the broken teeth-\n" + 
                "<i>A picture was added to your inventory</i>\n\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return.";
        }
        else if (imgPictureTeeth.enabled == true)
        {
            gameText.text = "There are no more clues here.\n\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return.";     
        }
    }

    void ReadAutopsy()
    {
        autopsyReport = true;
        if (imgNotes.enabled == false)
        {
            gameText.text = "It seems you don't have the autopsy. You should get it first.\n\n" +
                "Press <color=#ff0000ff>\"R\"</color> to return.";
        }
        else if (imgNotes.enabled == true)
        {
            imgAutopsy.enabled = true;
            gameText.text = "It seems like a normal suicide. However, something is not right...\n" +
                "I have to find more clues.\n\n" + 
                "Press <color=#ff0000ff>\"R\"</color> to return.";
        }
        
    }

    void CantEnter()
    {
        gameText.text = "The door is locked. You need a key to open it.\n\n" +
                            "Press <color=#ff0000ff>\"R\"</color> to return.";
    }

    void CheckInputs()
    {
        switch (currentStage)
        {
            case playersStage.body:
                if (Input.GetKeyDown(KeyCode.V)) { currentStage = playersStage.inspect_body; }
                if (Input.GetKeyDown(KeyCode.B))
                {
                    if (imgKey.enabled == true)
                    {
                        SceneManager.LoadScene("Bedroom");
                    }
                    else if (imgKey.enabled == false)
                    {
                        currentStage = playersStage.cant_enter;
                    }
                }
                if (Input.GetKeyDown(KeyCode.K)) { SceneManager.LoadScene("Kitchen"); }
                if (Input.GetKeyDown(KeyCode.R)) { SceneManager.LoadScene("Neighborhood"); }
                break;
            case playersStage.inspect_body:
                if (Input.GetKeyDown(KeyCode.A)) { currentStage = playersStage.read_autopsy; }
                if (Input.GetKeyDown(KeyCode.H)) { currentStage = playersStage.inspect_hand; }
                if (Input.GetKeyDown(KeyCode.C)) { currentStage = playersStage.inspect_head; }
                if (Input.GetKeyDown(KeyCode.M)) { currentStage = playersStage.inspect_mouth; }
                if (Input.GetKeyDown(KeyCode.R)) { currentStage = playersStage.body; }
                break;
            case playersStage.inspect_hand:
                if (Input.GetKeyDown(KeyCode.R))
                {
                    currentStage = playersStage.inspect_body;
                    handBruise = false;
                }
                break;
            case playersStage.inspect_head:
                if (Input.GetKeyDown(KeyCode.R)) { currentStage = playersStage.inspect_body; }
                break;
            case playersStage.inspect_mouth:
                if (Input.GetKeyDown(KeyCode.R))
                {
                    currentStage = playersStage.inspect_body;
                    brokenTeeth = false;
                }
                break;
            case playersStage.read_autopsy:
                if (Input.GetKeyDown(KeyCode.R))
                {
                    currentStage = playersStage.inspect_body;
                    imgAutopsy.enabled = false;
                }
                break;
            case playersStage.cant_enter:
                if (Input.GetKeyDown(KeyCode.R)) { currentStage = playersStage.body; }
                break;
        }
    }

    bool intToBool(int val)
    {
        if (val != 0)
            return true;
        else
            return false;
    }

}