Compare commits

..

No commits in common. "master" and "commissions" have entirely different histories.

235 changed files with 4940 additions and 5541 deletions

View file

@ -1,27 +0,0 @@
on:
push:
branches: [master]
jobs:
web:
runs-on: ubuntu-latest
env:
BUNDLE_GEMFILE: ${{ github.workspace }}/main/Gemfile
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Setup Ruby
uses: https://github.com/ruby/setup-ruby@v1
with:
ruby-version: 3.2.2
bundler-cache: true
- name: Build
run: cd main && bundle exec jekyll build
- name: Deploy
run: |
apt update && apt install -y rsync
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb && sudo dpkg -i cloudflared.deb
echo "${{ secrets.ELLBOT_KEY }}" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
rsync -rv --delete -e 'ssh -o "ProxyCommand cloudflared access ssh --hostname %h" -o "StrictHostKeyChecking=no"' main/_site/ ellbot@ssh.ellpeck.de:/var/www/ellpeck

9
.gitignore vendored
View file

@ -1,3 +1,6 @@
.jekyll-*
_site
.idea
node_modules
sitemap.xml
feed.json
rss.xml
atom.xml
blog-*.html

29
.htaccess Normal file
View file

@ -0,0 +1,29 @@
Options -Indexes
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule (.*) $1.html [L]
RewriteRule ^discord/?$ "https://discord.gg/Uy2ZM9X" [R=301,L]
RewriteRule ^actaddchangelog/?$ "https://github.com/Ellpeck/ActuallyAdditions/blob/main/update/changelog.md" [R=301,L]
RewriteRule ^actadddownload/?$ "https://minecraft.curseforge.com/projects/actually-additions/files" [R=301,L]
RewriteRule ^actaddlicense/?$ "https://github.com/Ellpeck/ActuallyAdditions/blob/main/LICENSE.md" [R=301,L]
RewriteRule ^actadd/?$ "https://minecraft.curseforge.com/projects/actually-additions" [R=301,L]
RewriteRule ^projects/?$ "https://ellpeck.de/#projects" [NE,R=301,L]
RewriteRule ^impressum/?$ "https://ellpeck.de/#impressum" [NE,R=301,L]
RewriteRule ^privacy/?$ "https://ellpeck.de/#privacy" [NE,R=301,L]
RewriteRule ^mc/?$ "https://ellpeck.de/minecraft-stuff" [R=301,L]
RewriteRule ^minecraft-stuff/?$ "https://ellpeck.de/projects" [R=301,L]
RewriteRule ^yt/?$ "https://www.youtube.com/c/ellpeck" [R=301,L]
RewriteRule ^wishlist/?$ "https://www.amazon.de/hz/wishlist/ls/LZO9Y2Z3VJ5Q?&sort=default" [R=301,L]
RewriteRule ^fftranslate/?$ "https://poeditor.com/join/project/ElzC23ecB6" [R=301,L]
ErrorDocument 404 /404.html
<FilesMatch "^(blog|sitemap|feed|atom|rss)">
ExpiresActive On
ExpiresDefault A1
Header append Cache-Control must-revalidate
</FilesMatch>

View file

@ -5,13 +5,12 @@
<meta charset="UTF-8">
<title>404</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
<link rel="icon" href="favicon.ico">
<style>
body {
font-family: Roboto, sans-serif;
font-display: swap;
font-family: Roboto;
background-color: #383838;
}
@ -48,7 +47,7 @@
<body>
<div class="content">
<h1>💔 Four Oh Four</h1>
<h1>Four Oh Four</h1>
<p>
There's nothing here, I'm sorry.
</p>
@ -56,18 +55,18 @@
<span id="message"></span>
<script>
const messages = [
"Maybe try a different place?",
"Do you want to know a secret?",
"This is a magical place",
"What were you trying to find?",
"Now my day is ruined",
"There's a place like this somewhere",
"I shall overcome this obstacle.",
"Why are you like this?",
"this is so sad alexa play despacito",
"The server is at a loss for what you were trying to find",
"oh no :(",
"The world is quiet here.",
'Maybe try a different place?',
'Do you want to know a secret?',
'This is a magical place',
'What were you trying to find?',
'Now my day is ruined',
'There\'s a place like this somewhere',
'I shall overcome this obstacle.',
'Why are you like this?',
'this is so sad alexa play despacito',
'The server is at a loss for what you were trying to find',
'oh no :(',
'The world is quiet here.',
"Instructions unclear, got site stuck in debug mode",
"Please fix",
"If we get this video to 400 likes, I'll put the site back up",
@ -75,7 +74,7 @@
];
let message = Math.floor(Math.random() * messages.length);
document.getElementById("message").innerHTML = `<em>${messages[message]}</em>`;
document.getElementById('message').innerHTML = '<em>' + messages[message] + '</em>';
</script>
</p>
<p class="go-home">
@ -87,4 +86,4 @@
</div>
</body>
</html>
</html>

24
Jenkinsfile vendored Normal file
View file

@ -0,0 +1,24 @@
pipeline {
agent any
stages {
stage('Pull') {
when {
branch 'master'
}
steps {
sh '''cd /var/www/ellpeck
git pull'''
}
}
stage('Node') {
when {
branch 'master'
}
steps {
sh '''cd /var/www/ellpeck/node
node blog.js
node rss.js'''
}
}
}
}

58
actaddmanual/index.html Normal file
View file

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
<link rel="stylesheet" href="style.css">
<title>Actually Additions Manual</title>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<script src="../scripts/util.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-150032076-2"></script>
<script>
window.dataLayer = window.dataLayer || [];
let gtag = function () {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-150032076-2');
</script>
<script data-ad-client="ca-pub-5754829579653773" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
</head>
<body>
<div id="sidebar">
<!-- Sponsor buttons -->
<div class="sellout">
<iframe src="https://github.com/sponsors/Ellpeck/button" title="Sponsor Ellpeck" height="32" width="116" style="border: 0;"></iframe>
<a href="https://www.patreon.com/bePatron?u=2494595" data-patreon-widget-type="become-patron-button"></a>
<script async src="https://c6.patreon.com/becomePatronButton.bundle.js"></script>
</div>
<hr>
</div>
<div id="content">
<div id="entries">
<!-- Cookie notification -->
<script src="../scripts/cookieinfo.js"></script>
</div>
<script src="index.js"></script>
</div>
<div id="footer">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-5754829579653773" data-ad-slot="1438750467" data-ad-format="horizontal" data-full-width-responsive="true"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
</body>
</html>

View file

@ -1,22 +1,22 @@
const replacements = new Map();
replacements.set(/<imp>([^<]*)<r>/g, function (content) {
return `<span class="imp">${content}</span>`;
return `<span class="imp">${content}</span>`
});
replacements.set(/<item>([^<]*)<r>/g, function (content) {
return `<span class="item">${content}</span>`;
return `<span class="item">${content}</span>`
});
replacements.set(/<n>/g, function (_content) {
return "<br>";
return "<br>"
});
replacements.set(/<i>([^<]*)<r>/g, function (content) {
return `<em>${content}</em>`;
return `<em>${content}</em>`
});
replacements.set(/<tifisgrin>([^<]*)<r>/g, function (content) {
return `<span class="tifisgrin">${content}</span>`;
return `<span class="tifisgrin">${content}</span>`
});
$.ajax({
url: "https://cdn.jsdelivr.net/gh/Ellpeck/ActuallyAdditions@1.12.2/src/main/resources/assets/actuallyadditions/lang/en_US.lang",
url: "https://gitcdn.xyz/repo/Ellpeck/ActuallyAdditions/main/src/main/resources/assets/actuallyadditions/lang/en_US.lang",
cache: false,
success: populateManual
});
@ -26,16 +26,17 @@ function populateManual(lang) {
let data = lang.substring(startIndex + 18);
let entries = $("#entries");
let sidebar = $("#sidebar-content");
let sidebar = $("#sidebar");
for (let topic of data.split("\n\n")) {
let lines = topic.split("\n");
let t = "<div class=\"entry rounded\">";
let t = '<div class="entry rounded">';
// first line is name
let name = lines[0].substring(lines[0].indexOf("=") + 1);
let id = lines[0].match(/chapter\.([^.]*)\.name/)[1];
let id = lines[0].match(/chapter\.([^\.]*)\.name/)[1];
sidebar.append(`<a href=#${id}>${name}</a>`);
t += `<h1 id="${id}">${name}</h1>`;
t += `<a class="anchor" id="${id}"></a>`;
t += `<h1>${name}</h1>`;
// following lines are content
for (let i = 1; i < lines.length; i++) {
let text = lines[i].substring(lines[i].indexOf("=") + 1);
@ -45,23 +46,21 @@ function populateManual(lang) {
entries.append(t);
}
// force-scroll to proper anchor
if (location.hash.startsWith("#")) {
let element = $(location.hash);
if (element.length) {
$("html, body").animate({
scrollTop: element.offset().top
}, 0);
}
}
sidebar.append("<hr>");
sidebar.append(`<a href="https://ellpeck.de">Main Site</a>`)
sidebar.append(`<a href="https://ellpeck.de/impressum">Impressum</a>`);
sidebar.append(`<a href="https://ellpeck.de/privacy">Privacy</a>`);
sidebar.append(`<a href="https://git.ellpeck.de/Ellpeck/Web">&copy; Ellpeck</a>`);
sidebar.append("<hr>");
forceToAnchor();
}
function replaceFormatting(text) {
for (let [k, v] of replacements.entries()) {
const finalV = v;
text = text.replace(k, function (_substring, group) {
return finalV(group);
return v(group);
});
}
return text;
}
}

View file

@ -1,9 +1,5 @@
html {
scroll-padding-top: 40px;
}
body {
font-family: Roboto, sans-serif;
font-family: Roboto;
margin-top: 30px;
margin-bottom: 30px;
background-color: #e2e2e2;
@ -24,7 +20,10 @@ body {
margin-top: 20px;
margin-left: auto;
margin-right: auto;
padding: 30px 40px;
padding-top: 30px;
padding-bottom: 30px;
padding-left: 40px;
padding-right: 40px;
background-color: white;
}
@ -74,10 +73,19 @@ body {
padding: 15px;
}
.anchor {
display: block;
position: relative;
top: -40px;
visibility: hidden;
}
.sellout {
display: flex;
align-items: center;
flex-direction: column;
display: block;
position: relative;
margin-left: auto;
margin-right: auto;
text-align: center;
}
@media (max-width: 768px) {
@ -121,4 +129,4 @@ body {
padding-left: 15px;
padding-right: 15px;
}
}
}

View file

@ -1,100 +1,92 @@
---
layout: blog
title: ⛔ Oh God, Please Don't Port Actually Additions
description: As Actually Additions celebrates its fifth birthday, I break down what I like and dislike about it.
tags: [Minecraft]
discuss: https://twitter.com/Ellpeck/status/1259600490377216002
---
On March 7 of this year, my overwhelmingly popular Minecraft mod Actually Additions [celebrated its fifth birthday](https://www.curseforge.com/minecraft/mc-mods/actually-additions/files/2229705). I personally made my last real contribution to the project [around mid-June 2017](https://github.com/Ellpeck/ActuallyAdditions/commits/main?after=896a082d747a3e19755ded1973544d59fa992787+244), so basically three years ago. Every now and again, though (and by that, I mean almost constantly), I still get asked about the project. The main question I get is: "Will you port Actually Additions to Minecraft 1.15?"
Here's the short answer: No, I won't. I haven't worked on the project in *three years*. But this post isn't about that. It's about why *you* probably shouldn't port it either.
I've grown a lot in the last five years. Yes, I've literally grown in the last five years - I'm 20 years old now. But I've also grown as a programmer, and more importantly, as a game designer. In this post, I'm going to reminisce about some features of Actually Additions, tell you why I added them, how I added them and what I'd do differently if I made the mod today. Since I can't be bothered opening a modpack instance, I'm just going to go off of the information from the [online manual](https://ellpeck.de/actaddmanual/) for this list.
# "Actually Additions"
When first starting out with the mod, it was called something like "Some Pretty Techy Stuff". I realized that was a *terrible* name choice though, so I changed it to "Some Pretty *Random* Stuff" pretty quickly. That wasn't much better, so after some (evidently not very much) consideration, I apparently settled on *the worst possible mod name*, "Actually Additions". It makes no grammatical sense unless you add a comma, and it's annoying to say for me because, as any non-native English speaker knows, saying "Actually" is very hard.
# Overpowered Items
A lot of items in Actually Additions suffer the exact same problem: They're *balanced because they take effort*. What that means is that they're overpowered beyond belief, because they usually take a somewhat large amount of resources to create (though that isn't even true for some of them) and then do something crazy at (basically) no cost. All of these items are ones I'd either implement completely differently today, or scrap entirely:
- The Fishing Net
- Greenhouse Glass
- The Item Repairer
- The Ring of Growth
## Oh God, Storage Crates
Why do people love them so much? They make every other mod's storage options *completely worthless*. Why they're not disabled in more modpacks is a complete mystery to me.
I don't think there's any code I've written in my life that I regret as much as Storage Crates. God, they're awful.
## Solar Panels
When I first implemented them, I thought passive power generation was perfectly fine. That opinion changed pretty quickly the more I got used to working with RF-based mods and the struggle of trying to balance them against each other. So... I changed solar panels to be ridiculously expensive and only generate 8 RF per tick, which is a rather small amount.
Still, a lot of people kept on using them. *Creative*.
If you really, *really* want to port Actually Additions to 1.15, at least remove all of these unbalanced features first. Or at least try to make them less horrible somehow.
# Phantomfaces
I still, to this day, *adore* the way I implemented Phantomfaces. They look good, they work well, they have unique functionality, but... **everyone uses them wrong**. They're not supposed to be a means of item transportation. How could I make them be more restricted to their intended use case? I don't really know, honestly.
# The Coffee Maker
Etho is my favorite YouTuber of all time, and always has been. When he first used the Coffee Maker in his HermitCraft modded series, I legitimately cried of happiness. Following that point, he made and used it in every modded series that it was available in.
I still think it's a great block with awesomely unique functionality, and I have been considering implementing something similar in functionality into my newer mod Nature's Aura.
# Canola and Oil
There's not much to say about the entire canola power generation system other than... I love it. I think it's a great way to generate power in almost every modpack. It's easy to set up, it scales well to higher tiers of power generation and it's a fun thing to automate. It makes me happy to see that a lot of people seem to still agree nowadays.
# Wings of the Bats
I'm not the *biggest* fan of creative flight in survival mode anymore in general, but I do like how I implemented the Wings of the Bats. For those of you who don't know: They have a charge that runs out when you fly with them. If you land on the ground, the charge slowly fills back up again. *But* if you fly up to the ceiling and hover there for a while, the charge fills up a lot faster than if you were to stand on the ground. Because you're a bat.
# AIOTS
...were a huge mistake. "All-In-One-Tools", originally called something ridiculous like "Paxelordoe" (Pickaxe, Shovel, Sword and Hoe), were pretty much intended to be a better version of Mekanism's paxels. I like the idea of all-in-one tools in general, but I think they should be later game. I also think they should *not* have **eight times** the durability of the tools they were made from.
# Laser Relays
Laser Relays were my sorry attempt at copying Immersive Engineering's awesome wires. Did I succeed in that? No. But did I still make something hella awesome? Yes. As with all of the other things on this list that I actually do like, they scale well and they look good.
I personally especially love the item laser relays, because they can do almost as much as logistics pipes can, minus the request terminal. Do people that use them realize that? Most of the time, no. Is there a way to fix that? Probably, but I don't know what it is.
# The Atomic Reconstructor
...is awesome, plain and simple. This is one of the features I added surprisingly late in development, probably a year or so after the mod originally came out, if I had to guess. The reason I like it so much is that I'm a huge fan of in-world crafting. It's less boring than sitting in front of some UI-heavy machine, and it's more fun to automate too. If I made Actually Additions again today, the Atomic Reconstructor would still be the mod's centerpiece. Also, the lenses are cool.
The crystals, though... I don't think I'd want them to work the same nowadays. The names were ridiculous, the textures were weird and the materials used to make them were generic and boring. Actually, I originally picked the ridiculous anagram-ish names specifically because I *knew* YouTubers would have trouble saying them, and the thought of that was hilarious to me.
# Lush Caves
They don't fit the mod (because it's not a biome/world gen mod), so most modpacks disable them. I don't really understand why, though, because they add so much to the world!
This game desperately needs a cave update.
# The Textures
The textures for Actually Additions were created by three separate artists, as well as *me*, the latter of which is very far from an artist.
That's why the visual style of the mod is inconsistent and honestly pretty gross. The textures of blocks don't fit together, most of them are flat and uninspired. The mod doesn't use that many nice particles and other visual effects to enhance the look and feel of mechanics.
Altogether, when playing the mod now, the texture and overall visual style of the mod make me feel like I'm playing some bizarre combination of old ruins, a tech mod and a confused magic mod.
# Honorable-ish Mentions
**Black Quartz**. Why didn't I make it drop the item directly instead of the ore? I don't know. Weird choice, though.
**Smiley Clouds**. God, they're creepy. Yes, they were just supposed to be a clone of Botania's Tiny Potato. Were they as well designed? No. Were they as cool? No. Would I ever want them in my life ever again? Definitely not.
**Treasure Chests**. I like the feature, but... does anyone even know they exist?
**ESDs** were actually one of the first features in the mod. I still like them, though their UI is clunky and hard to understand. If I made Actually Additions today, I'd definitely include them, but with a different name and a cleaner UI.
**Experience Solidifiers** are great. I like the solidified experience too, though I know that a lot of modpack makers seem to disagree, as it's disabled in almost every pack. I'd still want them today, though.
**The Feeder** is an almost exact copy of the Railcraft Feeding Station thing, because back in the day, I was so unoriginal that I just stole other people's content. Yea.
**Drills**. Love 'em. The upgrade system is neat and scales well, they use a good amount of power and they just look *so cool*.
**Balls of Fur** are actually called "hairy balls." I don't know if the fact that I *didn't* realize that that wasn't the best name when implementing them makes this whole situation less embarrassing or more embarrassing.
**The Empowerer** is still pretty cool in my opinion. I'd probably use it as a crafting mechanic again.
# What Do We Learn From This?
Well, I don't know if you learn anything from this, but there's one thing I learned for sure: I'm a lot better of a programmer and a lot better of a game designer now.
Sure, I'd still consider Actually Additions to be a pretty good mod, and it's definitely a nice project to be known for. But I don't think I'd do almost anything in the mod the same way nowadays.
Also, I kind of think that Actually Additions has run its course in some way; it's lived its life. It's important to remember that there is a huge amount of new, unexplored mod ideas still out there, and that there are already some great mods that implement unique, new ideas. Shouldn't we let those mods step into the spotlight now?
On March 7 of this year, my overwhelmingly popular Minecraft mod Actually Additions [celebrated its fifth birthday](https://www.curseforge.com/minecraft/mc-mods/actually-additions/files/2229705). I personally made my last real contribution to the project [around mid-June 2017](https://github.com/Ellpeck/ActuallyAdditions/commits/main?after=896a082d747a3e19755ded1973544d59fa992787+244), so basically three years ago. Every now and again, though (and by that, I mean almost constantly), I still get asked about the project. The main question I get is: "Will you port Actually Additions to Minecraft 1.15?"
Here's the short answer: No, I won't. I haven't worked on the project in *three years*. But this post isn't about that. It's about why *you* probably shouldn't port it either.
I've grown a lot in the last five years. Yes, I've literally grown in the last five years - I'm 20 years old now. But I've also grown as a programmer, and more importantly, as a game designer. In this post, I'm going to reminisce about some features of Actually Additions, tell you why I added them, how I added them and what I'd do differently if I made the mod today. Since I can't be bothered opening a modpack instance, I'm just going to go off of the information from the [online manual](https://www.ellpeck.de/actaddmanual/) for this list.
# "Actually Additions"
When first starting out with the mod, it was called something like "Some Pretty Techy Stuff". I realized that was a *terrible* name choice though, so I changed it to "Some Pretty *Random* Stuff" pretty quickly. That wasn't much better, so after some (evidently not very much) consideration, I apparently settled on *the worst possible mod name*, "Actually Additions". It makes no grammatical sense unless you add a comma, and it's annoying to say for me because, as any non-native English speaker knows, saying "Actually" is very hard.
# Overpowered Items
A lot of items in Actually Additions suffer the exact same problem: They're *balanced because they take effort*. What that means is that they're overpowered beyond belief, because they usually take a somewhat large amount of resources to create (though that isn't even true for some of them) and then do something crazy at (basically) no cost. All of these items are ones I'd either implement completely differently today, or scrap entirely:
- The Fishing Net
- Greenhouse Glass
- The Item Repairer
- The Ring of Growth
## Oh God, Storage Crates
Why do people love them so much? They make every other mod's storage options *completely worthless*. Why they're not disabled in more modpacks is a complete mystery to me.
I don't think there's any code I've written in my life that I regret as much as Storage Crates. God, they're awful.
## Solar Panels
When I first implemented them, I thought passive power generation was perfectly fine. That opinion changed pretty quickly the more I got used to working with RF-based mods and the struggle of trying to balance them against each other. So... I changed solar panels to be ridiculously expensive and only generate 8 RF per tick, which is a rather small amount.
Still, a lot of people kept on using them. *Creative*.
If you really, *really* want to port Actually Additions to 1.15, at least remove all of these unbalanced features first. Or at least try to make them less horrible somehow.
# Phantomfaces
I still, to this day, *adore* the way I implemented Phantomfaces. They look good, they work well, they have unique functionality, but... **everyone uses them wrong**. They're not supposed to be a means of item transportation. How could I make them be more restricted to their intended use case? I don't really know, honestly.
# The Coffee Maker
Etho is my favorite YouTuber of all time, and always has been. When he first used the Coffee Maker in his HermitCraft modded series, I legitimately cried of happiness. Following that point, he made and used it in every modded series that it was available in.
I still think it's a great block with awesomely unique functionality, and I have been considering implementing something similar in functionality into my newer mod Nature's Aura.
# Canola and Oil
There's not much to say about the entire canola power generation system other than... I love it. I think it's a great way to generate power in almost every modpack. It's easy to set up, it scales well to higher tiers of power generation and it's a fun thing to automate. It makes me happy to see that a lot of people seem to still agree nowadays.
# Wings of the Bats
I'm not the *biggest* fan of creative flight in survival mode anymore in general, but I do like how I implemented the Wings of the Bats. For those of you who don't know: They have a charge that runs out when you fly with them. If you land on the ground, the charge slowly fills back up again. *But* if you fly up to the ceiling and hover there for a while, the charge fills up a lot faster than if you were to stand on the ground. Because you're a bat.
# AIOTS
...were a huge mistake. "All-In-One-Tools", originally called something ridiculous like "Paxelordoe" (Pickaxe, Shovel, Sword and Hoe), were pretty much intended to be a better version of Mekanism's paxels. I like the idea of all-in-one tools in general, but I think they should be later game. I also think they should *not* have **eight times** the durability of the tools they were made from.
# Laser Relays
Laser Relays were my sorry attempt at copying Immersive Engineering's awesome wires. Did I succeed in that? No. But did I still make something hella awesome? Yes. As with all of the other things on this list that I actually do like, they scale well and they look good.
I personally especially love the item laser relays, because they can do almost as much as logistics pipes can, minus the request terminal. Do people that use them realize that? Most of the time, no. Is there a way to fix that? Probably, but I don't know what it is.
# The Atomic Reconstructor
...is awesome, plain and simple. This is one of the features I added surprisingly late in development, probably a year or so after the mod originally came out, if I had to guess. The reason I like it so much is that I'm a huge fan of in-world crafting. It's less boring than sitting in front of some UI-heavy machine, and it's more fun to automate too. If I made Actually Additions again today, the Atomic Reconstructor would still be the mod's centerpiece. Also, the lenses are cool.
The crystals, though... I don't think I'd want them to work the same nowadays. The names were ridiculous, the textures were weird and the materials used to make them were generic and boring. Actually, I originally picked the ridiculous anagram-ish names specifically because I *knew* YouTubers would have trouble saying them, and the thought of that was hilarious to me.
# Lush Caves
They don't fit the mod (because it's not a biome/world gen mod), so most modpacks disable them. I don't really understand why, though, because they add so much to the world!
This game desperately needs a cave update.
# The Textures
The textures for Actually Additions were created by three separate artists, as well as *me*, the latter of which is very far from an artist.
That's why the visual style of the mod is inconsistent and honestly pretty gross. The textures of blocks don't fit together, most of them are flat and uninspired. The mod doesn't use that many nice particles and other visual effects to enhance the look and feel of mechanics.
Altogether, when playing the mod now, the texture and overall visual style of the mod make me feel like I'm playing some bizarre combination of old ruins, a tech mod and a confused magic mod.
# Honorable-ish Mentions
**Black Quartz**. Why didn't I make it drop the item directly instead of the ore? I don't know. Weird choice, though.
**Smiley Clouds**. God, they're creepy. Yes, they were just supposed to be a clone of Botania's Tiny Potato. Were they as well designed? No. Were they as cool? No. Would I ever want them in my life ever again? Definitely not.
**Treasure Chests**. I like the feature, but... does anyone even know they exist?
**ESDs** were actually one of the first features in the mod. I still like them, though their UI is clunky and hard to understand. If I made Actually Additions today, I'd definitely include them, but with a different name and a cleaner UI.
**Experience Solidifiers** are great. I like the solidified experience too, though I know that a lot of modpack makers seem to disagree, as it's disabled in almost every pack. I'd still want them today, though.
**The Feeder** is an almost exact copy of the Railcraft Feeding Station thing, because back in the day, I was so unoriginal that I just stole other people's content. Yea.
**Drills**. Love 'em. The upgrade system is neat and scales well, they use a good amount of power and they just look *so cool*.
**Balls of Fur** are actually called "hairy balls." I don't know if the fact that I *didn't* realize that that wasn't the best name when implementing them makes this whole situation less embarrassing or more embarrassing.
**The Empowerer** is still pretty cool in my opinion. I'd probably use it as a crafting mechanic again.
# What Do We Learn From This?
Well, I don't know if you learn anything from this, but there's one thing I learned for sure: I'm a lot better of a programmer and a lot better of a game designer now.
Sure, I'd still consider Actually Additions to be a pretty good mod, and it's definitely a nice project to be known for. But I don't think I'd do almost anything in the mod the same way nowadays.
Also, I kind of think that Actually Additions has run its course in some way; it's lived its life. It's important to remember that there is a huge amount of new, unexplored mod ideas still out there, and that there are already some great mods that implement unique, new ideas. Shouldn't we let those mods step into the spotlight now?

View file

@ -1,54 +1,46 @@
---
layout: blog
title: ⚔️ Big Projects
description: "How a once small project I even created a post about turned into the first game I'm selling: Foe Frenzy"
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1173247686654517249
---
So I'm releasing a game soon. In December, to be exact. Here's some stuff I did and learned after telling myself that [Foe Frenzy](https://ellpeck.itch.io/FoeFrenzy) would be a [Small Project](https://ellpeck.de/#blog-small_projects) until it suddenly became the first game I'd ever release as a commercial product.
# In Discord
After deciding that I'd only be selling my game on itch.io, I quickly realized that that might not be the best of ideas in the long run, because few people actually know about the platform - despite it being pretty great.
So I decided to also sell the game on Discord - the Skype-Teamspeak-IRC-hybrid that recently also became a game store. As it turns out, this was *surprisingly* easy: All you have to do is pay $25, go through a really well organized checklist that is provided in Discord's usual jokey writing style and submit. Tada! Your game's on Discord now.
By the way, in case you actually want to see what Foe Frenzy's Discord store page currently looks like before release, you should [join my Discord](https://link.ellpeck.de/discordweb) and check out its `#ff-store` channel (on Desktop). It looks pretty snazzy!
## Oh, also:
As it turns out, Discord has a game SDK. And Achievement support. And Lobbies, and Multiplayer, and Invites.
And surprisingly, integrating all of that with Foe Frenzy wasn't necessarily the breeziest breeze ever, as I didn't really have any example implementations or anything of the sort at hand, but it worked out in the end!
Foe Frenzy will have full compatibility with Discord's Multiplayer features. What does this include, you ask?
* After hosting a Multiplayer game from Foe Frenzy, you can invite your friends to play with you through the simple click of a button: <blockquote class="twitter-tweet"><p lang="en" dir="ltr">You&#39;ll also be able to easily invite people from inside the game! <a href="https://t.co/f3dixbwnxD">pic.twitter.com/f3dixbwnxD</a></p>&mdash; Ell (@Ellpeck) <a href="https://twitter.com/Ellpeck/status/1172990814072557573?ref_src=twsrc%5Etfw">September 14, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
* When receiving an invite, you can simply click the Join button which will launch the game and instantly send you into your friend's online lobby: <blockquote class="twitter-tweet"><p lang="en" dir="ltr">SOMETHING IS COMING <a href="https://t.co/ZWVUuRDhD3">pic.twitter.com/ZWVUuRDhD3</a></p>&mdash; Ell (@Ellpeck) <a href="https://twitter.com/Ellpeck/status/1172954763803136001?ref_src=twsrc%5Etfw">September 14, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
* From there, you can play online without needing to expose your IP, fiddle around with your router settings or really do anything else.
* And have I mentioned... you'll be able to use these features even when buying the game from itch.io, you'll be able to combine Discord and IP-based multiplayer, and you'll even be able to combine online multiplayer and local multiplayer! *So many possibilities!*
So yea. That's pretty exciting.
# Music and Trailer
So for a rather long time now, I've been watching a YouTuber called [ThinMatrix](https://youtube.com/ThinMatrix) develop his own game, and simultaneously listening to the amazing music for said game, created by the lovely [Jamal Green](https://twitter.com/JamalGreenMusic).
So after some contemplating (and a lot of anxiety), I decided to send him an email, and quite quickly, he replied and said that, yes indeed, he would love to create the soundtrack for Foe Frenzy. So that's what he's doing now, and it's super awesome.
In case you haven't seen it yet, here's the announcement trailer for the game which features the awesome trailer theme that he created:
<iframe width="560" height="315" src="https://www.youtube.com/embed/Z7ZeuVBNuf4" frameborder="0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
So the moral of that is basically: If you like an artist and want to cooperate with them, don't be scared to message them - the worst that can happen is they say no, and the best that can happen is that they create an awesome soundtrack for you!
# The whole platform issue
If you read all of my blog posts, you'll know about the previous one I made about me trying to figure out how to get a MonoGame project to work well cross-platform without having to upgrade to the not-yet-well-supported .NET Core.
As it turns out, all I had to do was actually use versions of .NET Framework and netstandard (which is another build target that can be quite confusing to people that aren't used to the whole .NET environment and the weird state it's currently in) that actually, you know, *are compatible with each other*. So after sorting that out, I made [a little tool](https://github.com/Ellpeck/MonoKickstartAutoBundle) that allows you to easily pack any .NET Framework application with Mono without having to even do so much as break a sweat. So that was quite handy.
# Oh God, what if no one buys my game
Obviously, I have a lot of anxiety, as my Twitter feed, the `#feels` channel on my Discord and my therapist make pretty clear.
So a feeling I have almost *constantly* is the fear that my game won't go well. That no one will care, that no one will buy it, that no one will enjoy it, that it'll be rated horribly, that YouTubers will make fun of it, that... honestly, so many more stupid fears. And obviously, the release hasn't happened yet, and so those fears are still there. But the best advice I can give is to just power through those fears if you also have them, because, as with the e-mail, the worst thing that can happen is that they come true - but the best thing that can happen is that they won't. So, really, the only way it can go is up.
But also, in case you *do* buy the game once it comes out, and in case you also like it, please tell me. It would mean the world to me. Thanks so much.
Also, thanks for reading. <3
So I'm releasing a game soon. In December, to be exact. Here's some stuff I did and learned after telling myself that [Foe Frenzy](https://ellpeck.itch.io/FoeFrenzy) would be a [Small Project](https://ellpeck.de/#blog-small_projects) until it suddenly became the first game I'd ever release as a commercial product.
# In Discord
After deciding that I'd only be selling my game on itch.io, I quickly realized that that might not be the best of ideas in the long run, because few people actually know about the platform - despite it being pretty great.
So I decided to also sell the game on Discord - the Skype-Teamspeak-IRC-hybrid that recently also became a game store. As it turns out, this was *surprisingly* easy: All you have to do is pay $25, go through a really well organized checklist that is provided in Discord's usual jokey writing style and submit. Tada! Your game's on Discord now.
By the way, in case you actually want to see what Foe Frenzy's Discord store page currently looks like before release, you should [join my Discord](https://ellpeck.de/discord) and check out its `#ff-store` channel (on Desktop). It looks pretty snazzy!
## Oh, also:
As it turns out, Discord has a game SDK. And Achievement support. And Lobbies, and Multiplayer, and Invites.
And surprisingly, integrating all of that with Foe Frenzy wasn't necessarily the breeziest breeze ever, as I didn't really have any example implementations or anything of the sort at hand, but it worked out in the end!
Foe Frenzy will have full compatibility with Discord's Multiplayer features. What does this include, you ask?
* After hosting a Multiplayer game from Foe Frenzy, you can invite your friends to play with you through the simple click of a button: <blockquote class="twitter-tweet"><p lang="en" dir="ltr">You&#39;ll also be able to easily invite people from inside the game! <a href="https://t.co/f3dixbwnxD">pic.twitter.com/f3dixbwnxD</a></p>&mdash; Ell (@Ellpeck) <a href="https://twitter.com/Ellpeck/status/1172990814072557573?ref_src=twsrc%5Etfw">September 14, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
* When receiving an invite, you can simply click the Join button which will launch the game and instantly send you into your friend's online lobby: <blockquote class="twitter-tweet"><p lang="en" dir="ltr">SOMETHING IS COMING <a href="https://t.co/ZWVUuRDhD3">pic.twitter.com/ZWVUuRDhD3</a></p>&mdash; Ell (@Ellpeck) <a href="https://twitter.com/Ellpeck/status/1172954763803136001?ref_src=twsrc%5Etfw">September 14, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
* From there, you can play online without needing to expose your IP, fiddle around with your router settings or really do anything else.
* And have I mentioned... you'll be able to use these features even when buying the game from itch.io, you'll be able to combine Discord and IP-based multiplayer, and you'll even be able to combine online multiplayer and local multiplayer! *So many possibilities!*
So yea. That's pretty exciting.
# Music and Trailer
So for a rather long time now, I've been watching a YouTuber called [ThinMatrix](https://youtube.com/ThinMatrix) develop his own game, and simultaneously listening to the amazing music for said game, created by the lovely [Jamal Green](https://twitter.com/JamalGreenMusic).
So after some contemplating (and a lot of anxiety), I decided to send him an email, and quite quickly, he replied and said that, yes indeed, he would love to create the soundtrack for Foe Frenzy. So that's what he's doing now, and it's super awesome.
In case you haven't seen it yet, here's the announcement trailer for the game which features the awesome trailer theme that he created:
<iframe width="560" height="315" src="https://www.youtube.com/embed/Z7ZeuVBNuf4" frameborder="0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
So the moral of that is basically: If you like an artist and want to cooperate with them, don't be scared to message them - the worst that can happen is they say no, and the best that can happen is that they create an awesome soundtrack for you!
# The whole platform issue
If you read all of my blog posts, you'll know about the previous one I made about me trying to figure out how to get a MonoGame project to work well cross-platform without having to upgrade to the not-yet-well-supported .NET Core.
As it turns out, all I had to do was actually use versions of .NET Framework and netstandard (which is another build target that can be quite confusing to people that aren't used to the whole .NET environment and the weird state it's currently in) that actually, you know, *are compatible with each other*. So after sorting that out, I made [a little tool](https://github.com/Ellpeck/MonoKickstartAutoBundle) that allows you to easily pack any .NET Framework application with Mono without having to even do so much as break a sweat. So that was quite handy.
# Oh God, what if no one buys my game
Obviously, I have a lot of anxiety, as my Twitter feed, the `#feels` channel on my Discord and my therapist make pretty clear.
So a feeling I have almost *constantly* is the fear that my game won't go well. That no one will care, that no one will buy it, that no one will enjoy it, that it'll be rated horribly, that YouTubers will make fun of it, that... honestly, so many more stupid fears. And obviously, the release hasn't happened yet, and so those fears are still there. But the best advice I can give is to just power through those fears if you also have them, because, as with the e-mail, the worst thing that can happen is that they come true - but the best thing that can happen is that they won't. So, really, the only way it can go is up.
But also, in case you *do* buy the game once it comes out, and in case you also like it, please tell me. It would mean the world to me. Thanks so much.
Also, thanks for reading. <3

View file

@ -1,15 +1,6 @@
---
layout: blog
title: ✨ Blogs are Cool, I Think
description: The first post and how I created it
tags: [Miscellaneous]
discuss: https://twitter.com/Ellpeck/status/1096937184601538566
archived: true
---
So I've been wanting to make a blog for a while, but never found the motivation to do so. Especially with all of the blog softwares out there, it was hard to figure out which one to use to make it fit this website and its design nicely.
I didn't really want to make a whole different page just for the blog because it would kind of throw the design off, so I took some inspiration from [Vazkii's blog](https://vazkii.us/#blog) and made mine work in a similar fashion. This still made it work with the single page design.
Now because I'm not good with PHP at all, I actually made this entire thing using only JavaScript. It's.. a bit of a mess, honestly, but it works.
So I've been wanting to make a blog for a while, but never found the motivation to do so. Especially with all of the blog softwares out there, it was hard to figure out which one to use to make it fit this website and its design nicely.
I didn't really want to make a whole different page just for the blog because it would kind of throw the design off, so I took some inspiration from [Vazkii's blog](https://vazkii.us/#blog) and made mine work in a similar fashion. This still made it work with the single page design.
Now because I'm not good with PHP at all, I actually made this entire thing using only JavaScript. It's.. a bit of a mess, honestly, but it works.
So here it is. Expect me to, very occasionally, do a post about one or the other random thing. *Enjoy!*

View file

@ -1,22 +1,15 @@
---
layout: blog
title: 🤷 But Do You Really Care?
description: On taking a break from social media
tags: [Miscellaneous]
---
Over the years, my views on social media and its purpose have changed many times. When I first started out using the internet, playing Minecraft and watching YouTube videos about it, I didn't really care about social media in general. Eventually, I created a Twitter account, just to be able to follow people and see their tweets, to see what the people I cared about were... caring about. Eventually, as Discord rolled around, I first just used it to talk to my friends, but I eventually decided that a server would probably be a good idea. So I created the "Ellpeck and Friends" server, which eventually evolved into the server [that it is today](https://link.ellpeck.de/discordweb).
So, the more I used Discord and Twitter, the more... *obsessive* I became about them. Every time I'd write a tweet, I'd sit there, staring at my screen for the better part of half an hour, seeing if anyone would reply, if anyone would like the tweet, if anyone would retweet, if anyone had an opinion on what I was saying. *If anyone approved*. Every time I'd say something in my Discord server, I'd actively wait for people to respond. To answer my question, to react to my joke. I wanted to know *if anyone approved*. Every time I saw that little red dot appear next to the Discord icon in the taskbar, I'd instantly stop whatever I was doing and figuratively *run* into my server to see what was going on. Did someone need my help? Did someone *finally* reply to that one message I posted two hours ago? No, it was just a new server member who joined, triggering one of those weird "a new user joined" messages.
But... why? Why would I care so much about who liked my tweet, or who replied to it? Even if my close friends liked it, I would go "AHA, someone approves!", despite the fact that I *know* that my friends approve of most of the things I do - that's kind of what friends just do. Why did I care so much about a stupid message in my stupid Discord server? Why did I *desperately* want the person that just posted "when will you update Actually Additions to 1.15?" to reply to what I answered? They were just a stranger. It's not like their opinion actually matters when it comes to me and my life. But somehow, it still mattered *to me*.
So the other day, I just decided... no. This wasn't what I was going to do anymore. I wasn't going to let some dumb, scream-into-the-void social media platforms control my life anymore. So, before bed that night, I quickly deleted Discord and Twitter off of my computer, and off of my phone. Ever since, my phone's homescreen has had two blatantly visible empty spaces.
It's been about two weeks since I did that, and... I've honestly been feeling really good about it. I started occasionally checking Twitter again, but never just to see notifications or to see if someone replied to any of my tweets. I only ever look at it if I want to tweet, or if I'm curious about what someone else has been tweeting about recently. Last night, I opened Discord from the browser to check if any of my friends have been messaging me there. There were a couple of notifications, which I replied to. I also checked my server, and *oh my God*, did I feel good about taking a break from that. There were a bunch of messages from people asking about my mods, and how to do certain things. There weren't any important messages, anything that, if I missed it, would change my life in some way. I didn't *need* to be there.
I realized that I shouldn't care that much what other people think about me; neither on social media, nor in real life. It's an unnecessary hassle to make sure that everyone loves you, that you don't make any mistakes, that you only have opinions that other people approve of. The important part is that you approve of *yourself*, and that the people you care about also care about you.
So... if you're feeling like there's a lot of pressure on you whenever you tweet, or send a message in a Discord server, or write a Facebook post, then maybe you should try just not doing that for a little while. It might make you feel a bit better.
*This post won't have a discussion link, for obvious reasons.*
Over the years, my views on social media and its purpose have changed many times. When I first started out using the internet, playing Minecraft and watching YouTube videos about it, I didn't really care about social media in general. Eventually, I created a Twitter account, just to be able to follow people and see their tweets, to see what the people I cared about were... caring about. Eventually, as Discord rolled around, I first just used it to talk to my friends, but I eventually decided that a server would probably be a good idea. So I created the "Ellpeck and Friends" server, which eventually evolved into the server [that it is today](https://ellpeck.de/discord).
So, the more I used Discord and Twitter, the more... *obsessive* I became about them. Every time I'd write a tweet, I'd sit there, staring at my screen for the better part of half an hour, seeing if anyone would reply, if anyone would like the tweet, if anyone would retweet, if anyone had an opinion on what I was saying. *If anyone approved*. Every time I'd say something in my Discord server, I'd actively wait for people to respond. To answer my question, to react to my joke. I wanted to know *if anyone approved*. Every time I saw that little red dot appear next to the Discord icon in the taskbar, I'd instantly stop whatever I was doing and figuratively *run* into my server to see what was going on. Did someone need my help? Did someone *finally* reply to that one message I posted two hours ago? No, it was just a new server member who joined, triggering one of those weird "a new user joined" messages.
But... why? Why would I care so much about who liked my tweet, or who replied to it? Even if my close friends liked it, I would go "AHA, someone approves!", despite the fact that I *know* that my friends approve of most of the things I do - that's kind of what friends just do. Why did I care so much about a stupid message in my stupid Discord server? Why did I *desperately* want the person that just posted "when will you update Actually Additions to 1.15?" to reply to what I answered? They were just a stranger. It's not like their opinion actually matters when it comes to me and my life. But somehow, it still mattered *to me*.
So the other day, I just decided... no. This wasn't what I was going to do anymore. I wasn't going to let some dumb, scream-into-the-void social media platforms control my life anymore. So, before bed that night, I quickly deleted Discord and Twitter off of my computer, and off of my phone. Ever since, my phone's homescreen has had two blatantly visible empty spaces.
It's been about two weeks since I did that, and... I've honestly been feeling really good about it. I started occasionally checking Twitter again, but never just to see notifications or to see if someone replied to any of my tweets. I only ever look at it if I want to tweet, or if I'm curious about what someone else has been tweeting about recently. Last night, I opened Discord from the browser to check if any of my friends have been messaging me there. There were a couple of notifications, which I replied to. I also checked my server, and *oh my God*, did I feel good about taking a break from that. There were a bunch of messages from people asking about my mods, and how to do certain things. There weren't any important messages, anything that, if I missed it, would change my life in some way. I didn't *need* to be there.
I realized that I shouldn't care that much what other people think about me; neither on social media, nor in real life. It's an unnecessary hassle to make sure that everyone loves you, that you don't make any mistakes, that you only have opinions that other people approve of. The important part is that you approve of *yourself*, and that the people you care about also care about you.
So... if you're feeling like there's a lot of pressure on you whenever you tweet, or send a message in a Discord server, or write a Facebook post, then maybe you should try just not doing that for a little while. It might make you feel a bit better.
*This post won't have a discussion link, for obvious reasons.*

View file

@ -1,74 +1,65 @@
---
layout: blog
title: 😔 About Cross-Platform and Motivation
description: How moving from Java to C# taught me how horrible it is to create a cross-platform application with little to no knowledge or documentation
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1147502654236573697
archived: true
---
As originally a java kid, I've never had much trouble with making any of my programs work on Windows, Linux and Mac. With Java, all you have to do is make a jar and then tell people to install Java on their machine. That's it. But oh boy, am I getting hit in the face hard now that I'm making a game with MonoGame and .NET Framework.
# The story, with all its horrible parts
Imagine you're me. Imagine you're just happily making a game and eventually you decide that you want to distribute it to testers.
## The documentation
So you check the documentation of the framework you're using: MonoGame, in my case. It advertises itself as being amazing at cross-platform, so I look around to find a simple tutorial on what to do to get my game working on Mac and Linux.
Mono is where it's at. Okay. An open source implementation of .NET (or something like that) that works on Linux and Mac. Great! Now I just need to find out how to easily package that with my game, so that people on those operating systems don't have to install it on their own.
## Mono Kickstart
So there's a project on GitHub with a MonoGame fork (meaning they should work together flawlessly!) called Mono Kickstart. It says that you can just add the files to your project, rename a couple of them, change one or two lines in a bash script and then your game will run smoothly on Linux and Mac without having to install Mono.
Yea, no. Some random issue about `System.Runtime.dll` not being found, no trace of the issue online, no tutorials anywhere, the Kickstart repository doesn't have an Issues tab for some reason. Dead end.
## mkbundle
Searching some more online, I found a tool contained in Mono called mkbundle, that, apparently, you can use to bundle standalone applications for Windows, Linux and Mac by just running a simple command. Seems easy enough.
Yea, no. Installing Mono on Windows is a huge pain. mkbundle's target downloading doesn't work on Windows, so I have to go into some obscure folders, download some obscure zips, rename them, move them around until it finally works.
Hah. For some reason, mkbundle now requires me to install Visual Studio. So I install their commandline build tools that, after asking in my Discord, are apparently what mkbundle wants to use for something.
So then it finally works, and I have a... yea, no. Apparently now, mkbundle can't find `System.Runtime.dll`, just like Mono Kickstart couldn't before. So I search around in some folders and notice that that dll is in a sub-folder instead of the main folder it tries to look for it in, so I tell mkbundle that it's in that subfolder through a long and annoying addition to the command, and now it finally works.
Yea, just kidding. Now it crashes because it can't find `stdint.h`. It's neither telling me where it's trying to look for it, nor why it even needs it, what it is or really anything of use at all. Dead end.
## .NET Core
After some more looking, I find that .NET Core is basically a better version of .NET Framework (it's actually a lot different, but that doesn't matter in the scope of this post) that actually runs on multiple platforms natively.
So I go through the process of upgrading my game project from .NET Framework to .NET Core, and everything finally works. I can really easily use my IDE (Rider) to publish packaged builds of the game for Windows, Linux and Mac that don't even need the user to have .NET Core installed.
Except that it doesn't run on my Laptop for some strange reason. It runs fine on my PC, and it runs fine on my boyfriend's computer and his Linux VM, but my Laptop has some obscure issue about MonoGame not being able to load the shaders it requires. There are also no traces of the issue online, and after some looking around, I realize that MonoGame's .NET Core implementation is, according to the repository, just a hack that also hasn't been updated in over a year. Dead end.
(Also, I had a weird issue where, with the newest version of MonoGame for .NET Core, my game would randomly stutter, and while the MonoGame Discord was unhelpful and one person was frankly unreasonably rude in my opinion, I found out that it isn't the fault of my code (after plenty of profiling) and that the issue is easily fixed by downgrading to an earlier version. Why? Who even knows at this point.)
## So I guess I'll just force the user to install Mono themselves then
I tested around a bit after going back to .NET Framework and it turns out that installing Mono on Linux isn't actually that bad, and after you installed it, you don't even have to run a special command to get the game to run. All you have to do is go into a terminal and run the game's normal exe, and apparently mono figures itself out without needing to specifically call it. So that's great!
Except that, on Mac, it seems to be a whole nother story altogether. The one person I asked to try it on Mac installed Mono, but then the system didn't seem to realize that it was actually installed, as the `mono` command didn't work, and just running the game's exe directly also didn't work.
## Other stuff I tried
Because it's so fun, here's a list of other stuff that I tried to use:
* Some obscure NuGet package that's supposed to make an easy `msbuild` command to bundle the game. Also didn't work, but at least I submitted an issue to the project's GitHub repository. We'll see what happens, maybe this will be the saving grace.
* Some obscure program from three years ago, with outdated packages and outdated dependencies, that was supposed to make it really easy to bundle projects for all operating systems. It had the same `System.Runtime.dll` issue, but because it was so outdated anyway, I didn't even bother investigating why.
## Conclusion?
Honestly, I don't know what to do at this point. After finding out how horrible it is to make anything work cross-platform, I'm genuinely considering just rewriting the entire game using libgdx (which is a game framework for Java), or something.
There's no real conclusion to this, other than that it took me a whole lot of my time and energy and that it made me buy and eat way too many tubs of Ben & Jerry's ice cream, which is way too expensive, by the way.
# Also, the Multiplayer debacle
As a little side note, I recently added Online Multiplayer to the game in question, using the C# networking library Lidgren.
Its UPnP implementation doesn't seem to be with my router (or my something else), so I have to manually forward ports as if it were 2003.
Also, one of the testers has a completely different issue about incomplete packets that I have no idea about, and frankly, my energy to work on anything is so low at this point that I might as well just not care about it.
# Conclusion
If you're thinking about becoming an indie developer, you'll have to deal with this sort of stuff. And honestly, it's going to bring you down. Quite a lot.
A couple weeks ago, I picked up this project again after taking a long break from it out of lack of motivation, and after picking it back up, I was so happy and energized. I reworked the art to make it look really great, I added more sounds, I hired a music artist, I started implementing online multiplayer, I set a personal roadmap for release on itch.io later this year.
And then this whole thing happened, and now my motivation is back to zero. It's just extra frustrating to me because I was so excited about finally getting back the motivation that I had previously lost.
So yea. Thanks for reading, I guess. <3
As originally a java kid, I've never had much trouble with making any of my programs work on Windows, Linux and Mac. With Java, all you have to do is make a jar and then tell people to install Java on their machine. That's it. But oh boy, am I getting hit in the face hard now that I'm making a game with MonoGame and .NET Framework.
# The story, with all its horrible parts
Imagine you're me. Imagine you're just happily making a game and eventually you decide that you want to distribute it to testers.
## The documentation
So you check the documentation of the framework you're using: MonoGame, in my case. It advertises itself as being amazing at cross-platform, so I look around to find a simple tutorial on what to do to get my game working on Mac and Linux.
Mono is where it's at. Okay. An open source implementation of .NET (or something like that) that works on Linux and Mac. Great! Now I just need to find out how to easily package that with my game, so that people on those operating systems don't have to install it on their own.
## Mono Kickstart
So there's a project on GitHub with a MonoGame fork (meaning they should work together flawlessly!) called Mono Kickstart. It says that you can just add the files to your project, rename a couple of them, change one or two lines in a bash script and then your game will run smoothly on Linux and Mac without having to install Mono.
Yea, no. Some random issue about `System.Runtime.dll` not being found, no trace of the issue online, no tutorials anywhere, the Kickstart repository doesn't have an Issues tab for some reason. Dead end.
## mkbundle
Searching some more online, I found a tool contained in Mono called mkbundle, that, apparently, you can use to bundle standalone applications for Windows, Linux and Mac by just running a simple command. Seems easy enough.
Yea, no. Installing Mono on Windows is a huge pain. mkbundle's target downloading doesn't work on Windows, so I have to go into some obscure folders, download some obscure zips, rename them, move them around until it finally works.
Hah. For some reason, mkbundle now requires me to install Visual Studio. So I install their commandline build tools that, after asking in my Discord, are apparently what mkbundle wants to use for something.
So then it finally works, and I have a... yea, no. Apparently now, mkbundle can't find `System.Runtime.dll`, just like Mono Kickstart couldn't before. So I search around in some folders and notice that that dll is in a sub-folder instead of the main folder it tries to look for it in, so I tell mkbundle that it's in that subfolder through a long and annoying addition to the command, and now it finally works.
Yea, just kidding. Now it crashes because it can't find `stdint.h`. It's neither telling me where it's trying to look for it, nor why it even needs it, what it is or really anything of use at all. Dead end.
## .NET Core
After some more looking, I find that .NET Core is basically a better version of .NET Framework (it's actually a lot different, but that doesn't matter in the scope of this post) that actually runs on multiple platforms natively.
So I go through the process of upgrading my game project from .NET Framework to .NET Core, and everything finally works. I can really easily use my IDE (Rider) to publish packaged builds of the game for Windows, Linux and Mac that don't even need the user to have .NET Core installed.
Except that it doesn't run on my Laptop for some strange reason. It runs fine on my PC, and it runs fine on my boyfriend's computer and his Linux VM, but my Laptop has some obscure issue about MonoGame not being able to load the shaders it requires. There are also no traces of the issue online, and after some looking around, I realize that MonoGame's .NET Core implementation is, according to the repository, just a hack that also hasn't been updated in over a year. Dead end.
(Also, I had a weird issue where, with the newest version of MonoGame for .NET Core, my game would randomly stutter, and while the MonoGame Discord was unhelpful and one person was frankly unreasonably rude in my opinion, I found out that it isn't the fault of my code (after plenty of profiling) and that the issue is easily fixed by downgrading to an earlier version. Why? Who even knows at this point.)
## So I guess I'll just force the user to install Mono themselves then
I tested around a bit after going back to .NET Framework and it turns out that installing Mono on Linux isn't actually that bad, and after you installed it, you don't even have to run a special command to get the game to run. All you have to do is go into a terminal and run the game's normal exe, and apparently mono figures itself out without needing to specifically call it. So that's great!
Except that, on Mac, it seems to be a whole nother story altogether. The one person I asked to try it on Mac installed Mono, but then the system didn't seem to realize that it was actually installed, as the `mono` command didn't work, and just running the game's exe directly also didn't work.
## Other stuff I tried
Because it's so fun, here's a list of other stuff that I tried to use:
* Some obscure NuGet package that's supposed to make an easy `msbuild` command to bundle the game. Also didn't work, but at least I submitted an issue to the project's GitHub repository. We'll see what happens, maybe this will be the saving grace.
* Some obscure program from three years ago, with outdated packages and outdated dependencies, that was supposed to make it really easy to bundle projects for all operating systems. It had the same `System.Runtime.dll` issue, but because it was so outdated anyway, I didn't even bother investigating why.
## Conclusion?
Honestly, I don't know what to do at this point. After finding out how horrible it is to make anything work cross-platform, I'm genuinely considering just rewriting the entire game using libgdx (which is a game framework for Java), or something.
There's no real conclusion to this, other than that it took me a whole lot of my time and energy and that it made me buy and eat way too many tubs of Ben & Jerry's ice cream, which is way too expensive, by the way.
# Also, the Multiplayer debacle
As a little side note, I recently added Online Multiplayer to the game in question, using the C# networking library Lidgren.
Its UPnP implementation doesn't seem to be with my router (or my something else), so I have to manually forward ports as if it were 2003.
Also, one of the testers has a completely different issue about incomplete packets that I have no idea about, and frankly, my energy to work on anything is so low at this point that I might as well just not care about it.
# Conclusion
If you're thinking about becoming an indie developer, you'll have to deal with this sort of stuff. And honestly, it's going to bring you down. Quite a lot.
A couple weeks ago, I picked up this project again after taking a long break from it out of lack of motivation, and after picking it back up, I was so happy and energized. I reworked the art to make it look really great, I added more sounds, I hired a music artist, I started implementing online multiplayer, I set a personal roadmap for release on itch.io later this year.
And then this whole thing happened, and now my motivation is back to zero. It's just extra frustrating to me because I was so excited about finally getting back the motivation that I had previously lost.
So yea. Thanks for reading, I guess. <3

View file

@ -1,28 +1,6 @@
---
layout: blog
title: 🔮 The Future of Actually Additions
description: Not wanting to accept the fate of Actually Additions, someone has come to its rescue. 1.16, here we come?
tags: [Minecraft]
discuss: https://twitter.com/Ellpeck/status/1330938597785169925
---
*TLDR: Actually Additions for 1.16.4 in the future. No ETA. No Fabric port. No 1.13, 1.14 or 1.15. Beautiful art overhaul. Don't ask Ellpeck anything ever. Stay awesome.*
***TLDR: Actually Additions for 1.16.4 in the future. No ETA. No Fabric port. No 1.13, 1.14 or 1.15. Beautiful art overhaul. Don't ask Ellpeck anything ever. Stay awesome.***
# Updates to This Post
## March 2024
Development is [happening again](https://github.com/Ellpeck/ActuallyAdditions/commits/1.20.4/)! The, frankly, truly mad [Mrbysco](https://github.com/Mrbysco) has taken it upon himself to take porting matters into his own hands. [Flanks](https://github.com/Flanks255) is also helping. It's a whole thing. But yay!
## November 2023
Honestly, I don't know anymore. Progress has slowed to a halt, and with all the old-mod-renaissance stuff going on in 1.20, I'm semi-considering getting back into Actually Additions myself. I dunno. Let me know if you're interested in helping out or whatever.
## February 2023
Yes, the port is still being worked on. As pointed out numerous times, you can stay up to date with the port through [the `1.16` branch](https://github.com/Ellpeck/ActuallyAdditions/tree/1.16). Do not ask when the port will be finished. We don't know yet. It's a big task and the people working on it do so in their free time.
## November 2021
The port is still being actively worked on. You can stay up to date with the progress through [the `1.16` branch](https://github.com/Ellpeck/ActuallyAdditions/tree/1.16) on the GitHub repository.
# Why?
Before I start this post, I want to re-iterate that I haven't had a direct programming-related connection to Actually Additions in [over three years](https://github.com/Ellpeck/ActuallyAdditions/commits/main?after=896a082d747a3e19755ded1973544d59fa992787+244). I don't plan on changing this. This means that, if you have *anything* to say about the mod, be it issue reports or feature requests, **do not talk to me about that**. Go to [the issue tracker](https://github.com/Ellpeck/ActuallyAdditions/issues) or [my Discord](https://link.ellpeck.de/discordweb)'s `#minecraft` channel.
Before I start this post, I want to re-iterate that I haven't had a direct programming-related connection to Actually Additions in [over three years](https://github.com/Ellpeck/ActuallyAdditions/commits/main?after=896a082d747a3e19755ded1973544d59fa992787+244). I don't plan on changing this. This means that, if you have *anything* to say about the mod, be it issue reports or feature requests, **do not talk to me about that**. Go to [the issue tracker](https://github.com/Ellpeck/ActuallyAdditions/issues) or [my Discord](https://ellpeck.de/discord)'s `#minecraft` channel.
Okay, now that that's out of the way... let's talk about the future of Actually Additions. In [my last post](https://ellpeck.de/blog-actually_additions), I talked about the things I disliked about the mod and, in that same vein, the reasons that I don't want it to be updated beyond Minecraft 1.12. What I didn't necessarily make clear in that post is that this isn't really my decision, because I haven't touched Actually Additions in *a long time*. For the last few years, **Shadows-of-Fire** has been maintaining the mod (that is, fixing issues and publishing updates), which is also the reason that there haven't been any new features during that time. So what about now?
@ -36,15 +14,15 @@ So, as a result of that, **MiKeY** is basically the new, official maintainer of
A little while later, another person approached me: [Ridanisaurus](https://github.com/Ridanisaurus), who apparently really likes making beautiful art for mods. He asked me if I want new art for Actually Additions, and since I have some *opinions* about the current state of Actually Additions' graphics and visual consistency, I naturally said yes immediately. So yea, **Ridanisaurus** is basically the new, official artist of Actually Additions.
# What?
MiKeY and Rid are going to port Actually Additions to **Forge** for **Minecraft 1.16.4**. They are re-doing all of the art for the mod and they will also modify some of the features that I dislike, based on my personal feedback and the information outlined in [my diss track](https://ellpeck.de/blog-actually_additions).
MiKeY and Rid are going to port Actually Additions to **Forge** for **Minecraft 1.16.4**. They are re-doing all of the art for the mod and they will also modify some of the features that I dislike, based on my personal feedback and the information outlined in [my diss track](https://ellpeck.de/blog-actually_additions).
The art overhaul has already started (and even been finished, I believe), and because it is *gorgeous*, I'm going to show you some of the preview pictures that Rid has sent me right now. Enjoy.
![](faa_1.png)
![](blog/res/future_actually_additions/1.png =100%x*)
![](faa_2.png)
![](blog/res/future_actually_additions/2.png =100%x*)
![](faa_3.png)
![](blog/res/future_actually_additions/3.png =100%x*)
Don't they look *so good*?
@ -55,12 +33,12 @@ Since I know a lot of people will ask questions about this, here is a list of so
- We will *not* be keeping all of the features in the mod the same, especially not the Storage Crate, which will be getting a major overhaul.
- There will be *no* entirely new features, only changes to existing ones. Since this has been the case for the last few years as well, I don't think it's very surprising.
MiKeY has expressed great interest in helping me overhaul the features that I dislike and, as a result, making Actually Additions a better and more refined mod than it used to be.
MiKeY has expressed great interest in helping me overhaul the features that I dislike and, as a result, making Actually Additions a better and more refined mod than it used to be.
# When?
# When?
We don't know yet. *Please* don't ask.
# Thank You
So yea, that's about it for the news. I hope all of you are as excited about the port and the upcoming changes as I am. If you want to be kept up to date with the changes, you can check the [GitHub repository](https://github.com/Ellpeck/ActuallyAdditions) every once in a while (specifically [its `1.16` branch](https://github.com/Ellpeck/ActuallyAdditions/tree/1.16)) and join [my Discord server](https://link.ellpeck.de/discordweb), where we might post some updates from time to time.
So yea, that's about it for the news. I hope all of you are as excited about the port and the upcoming changes as I am. If you want to be kept up to date with the changes, you can check the [GitHub repository](https://github.com/Ellpeck/ActuallyAdditions) every once in a while (especially [its `clean-start` branch](https://github.com/Ellpeck/ActuallyAdditions/tree/clean-start)) and join [my Discord server](https://ellpeck.de/discord), where we might post some updates from time to time.
As an additional note, I would like to thank all of you for the continued support of not only Actually Additions, but all of my mods and even my non-Minecraft-related projects. Of course, there have also been some rude people, but all in all, yall have been an amazing community and I am so grateful that you enjoy the things I create. I love yall. ❤️
As an additional note, I would like to thank all of you for the continued support of not only Actually Additions, but all of my mods and even my non-Minecraft-related projects. Of course, there have also been some rude people, but all in all, yall have been an amazing community and I am so grateful that you enjoy the things I create. I love yall. ❤️

View file

@ -1,104 +1,97 @@
---
layout: blog
title: "☕ Java Tutorial, Part 1: Hello World"
description: The first part of my post series for programming beginners where I explain how to write code in Java.
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1182080078827737088
archived: true
---
So you want to learn to code. In my opinion, a good language for beginners is Java, because it's a rather easy to understand language, and it's cross-platform, meaning it can run on any operating system without the need for the programmer (you!) to change anything based on the platform. Also, Java is object-oriented (more on that later), meaning it's pretty good for writing any sort of simple or complex program.[^1]
So let's start!
# Setting it up
To actually be able to compile[^2] any Java code, you first need to download the [Java Development Kit](https://jdk.java.net/), or JDK for short. If you're a Linux or Mac user, you can just follow [their installation tutorial](https://openjdk.java.net/install/), and if you're on Windows, you can follow [this tutorial from Stack Overflow](https://stackoverflow.com/a/52531093). To check if the installation worked, you can just type `java -version` into a command prompt (which you can open on Windows by just typing `cmd` into your search).
Some people might disagree with me on this, but I recommend using an Integrated Development Environment (IDE) for development *especially* when you're just starting out. An IDE is a program that helps you with coding by suggesting changes, notifying you of errors in your code and also allows you to easily run and debug your application. My personal suggestion would be to install [IntelliJ IDEA](https://www.jetbrains.com/idea/), but you could also use other IDEs like [Eclipse](https://www.eclipse.org/downloads/).
The next thing you'll have to do is create a new project inside your IDE. For IntelliJ, all you have to do is click `New Project`, select `Java`, click `Next` twice and then type your project's name. I'm going to go with `JavaTutorial` for mine.
# Hello World
Once that's done, we can finally start programming. At first, you'll find that a lot of the stuff we're doing isn't explained directly, but I'll come back to some of the things I'm showing you now at a later point. Things like classes, methods and parameters probably won't make sense to you yet, but using them is required for even the most basic of running programs.
The first thing we want to do is create a simple program that outputs something back to the user inside of the program's console. To start, simply right click your `src` folder and select `New` and `Java Class` and give it a name like `Main`. I'll show you the code for it first and then give you a quick rundown of the important parts you need to know about it for now.
```java
public class Main {
public static void main(String[] args) {
System.out.println("Hello Tutorial World!");
}
}
```
That's it! If you now run your program (for IntelliJ, there should be a small green arrow to the left that you can click on), it'll print out the message to the console and then stop the program.
A lot of the code you see above doesn't *really* matter to you right now. The important stuff is this:
- Each instruction, each thing that the program should do, ends with a semicolon `;` and usually a new line, though that is optional.
- Any instruction you write between the inner curly braces `{}` will be executed in order of how it's written.
- `System.out.println("<enter anything here>")` is an instruction that causes a line of text to be written to the console.
As a little exercise, you can make your program output another line of text:
```java
public class Main {
public static void main(String[] args) {
System.out.println("Hello Tutorial World!");
System.out.println("This is another line of text!");
}
}
```
# Variables
Now the first thing you'll probably want to do is learn what variables are and how to use them. A variable, simply put, is any kind of value (a number, a string of text etc.) that can be changed and that has a name. In Java, each variable also has to have a fixed type: While a variable's *value* can be changed (say, from `some text` to `some other text`), its *type* cannot be changed (so a variable representing text cannot be changed into a variable representing a number). Variables are created as follows:
```java
public class Main {
public static void main(String[] args) {
System.out.println("Hello Tutorial World!");
System.out.println("This is another line of text!");
String someText;
int someNumber;
someText = "Some Text";
someNumber = 10;
}
}
```
As you can see from line 6 and 7, to declare a variable, you need to put the variable's type first (`String` meaning text, `int` meaning an integer; a number without a decimal point), followed by the variable's name, which you can choose yourself.
For now, these variables don't have a value assigned to them yet. That's done in line 9 and 10. To assign a value to a variable, you first put the variable's name, followed by an equals sign `=`, followed by your desired value. For numbers, you can simply put them down, but text has to be wrapped in quotes `""` (which is also why the text in lines 3 and 4 is wrapped in quotes).
To actually do something with the variables, let's have them be printed out to the console. *I've omitted the rest of the code here because it's getting quite long, but I'm just adding the following lines inside the inner braces `{}` below the already written code.*
```java
System.out.println(someText);
System.out.println(someNumber);
```
As you can see, to print a variable's value to the console, all you have to do is put its name where you would usually put a piece of text directly.
# Variable Manipulation
Obviously, this isn't really that exciting yet: Our program isn't really doing anything so far. Let's manipulate our variables. I've added the following code:
```java
someNumber = someNumber + 5;
System.out.println(someNumber);
```
As you can see, you can use a plus sign `+` (as well as other mathematical operators) to do math with numbers. The first statement changes `someNumber`'s value to a new value: `someNumber + 5`. Running the program will show you that the result is pretty obvious: 15 is printed, because 10 + 5 = 15.
The same kind of manipulation can be done with text, which is especially useful for printing variables to the console. The plus sign `+` chains two strings of text together.
```java
String chainedText = "The resulting value is " + someNumber;
System.out.println(chainedText);
```
Here, a new variable is created (`String chainedText`) and its value is set (`=`) to the shown string plus the value of the `someNumber` variable. When added to the rest of the code, running this code should display `The resulting value is 15`.
# Conclusion
Now obviously, this is just the beginning of what you can learn and do with Java. I feel like this is a good point to finish the first part of this tutorial. I know it's probably not super interesting yet, but bear with me for a little while and you'll pretty quickly be able to do some pretty cool stuff.
Before you click away, I'd like to ask you some questions that you can answer by clicking on the discussion link below this post:
- Could you follow this tutorial?
- What do you think about the pacing? Is it too fast or too slow, are there too many examples or too few?
- Do you want me to actually continue this tutorial series?
Anyway, thanks a lot for reading and happy coding! <3
[^1]: Also, it's what Minecraft mods are written in, which is probably the reason most of you are here.
[^2]: Compiling is what occurs when code written by you is converted into something that the computer can actually understand. More on that later, probably.
So you want to learn to code. In my opinion, a good language for beginners is Java, because it's a rather easy to understand language, and it's cross-platform, meaning it can run on any operating system without the need for the programmer (you!) to change anything based on the platform. Also, Java is object-oriented (more on that later), meaning it's pretty good for writing any sort of simple or complex program.[^1]
So let's start!
# Setting it up
To actually be able to compile[^2] any Java code, you first need to download the [Java Development Kit](https://jdk.java.net/), or JDK for short. If you're a Linux or Mac user, you can just follow [their installation tutorial](https://openjdk.java.net/install/), and if you're on Windows, you can follow [this tutorial from Stack Overflow](https://stackoverflow.com/a/52531093). To check if the installation worked, you can just type `java -version` into a command prompt (which you can open on Windows by just typing `cmd` into your search).
Some people might disagree with me on this, but I recommend using an Integrated Development Environment (IDE) for development *especially* when you're just starting out. An IDE is a program that helps you with coding by suggesting changes, notifying you of errors in your code and also allows you to easily run and debug your application. My personal suggestion would be to install [IntelliJ IDEA](https://www.jetbrains.com/idea/), but you could also use other IDEs like [Eclipse](https://www.eclipse.org/downloads/).
The next thing you'll have to do is create a new project inside your IDE. For IntelliJ, all you have to do is click `New Project`, select `Java`, click `Next` twice and then type your project's name. I'm going to go with `JavaTutorial` for mine.
# Hello World
Once that's done, we can finally start programming. At first, you'll find that a lot of the stuff we're doing isn't explained directly, but I'll come back to some of the things I'm showing you now at a later point. Things like classes, methods and parameters probably won't make sense to you yet, but using them is required for even the most basic of running programs.
The first thing we want to do is create a simple program that outputs something back to the user inside of the program's console. To start, simply right click your `src` folder and select `New` and `Java Class` and give it a name like `Main`. I'll show you the code for it first and then give you a quick rundown of the important parts you need to know about it for now.
```java
public class Main {
public static void main(String[] args) {
System.out.println("Hello Tutorial World!");
}
}
```
That's it! If you now run your program (for IntelliJ, there should be a small green arrow to the left that you can click on), it'll print out the message to the console and then stop the program.
A lot of the code you see above doesn't *really* matter to you right now. The important stuff is this:
- Each instruction, each thing that the program should do, ends with a semicolon `;` and usually a new line, though that is optional.
- Any instruction you write between the inner curly braces `{}` will be executed in order of how it's written.
- `System.out.println("<enter anything here>")` is an instruction that causes a line of text to be written to the console.
As a little exercise, you can make your program output another line of text:
```java
public class Main {
public static void main(String[] args) {
System.out.println("Hello Tutorial World!");
System.out.println("This is another line of text!");
}
}
```
# Variables
Now the first thing you'll probably want to do is learn what variables are and how to use them. A variable, simply put, is any kind of value (a number, a string of text etc.) that can be changed and that has a name. In Java, each variable also has to have a fixed type: While a variable's *value* can be changed (say, from `some text` to `some other text`), its *type* cannot be changed (so a variable representing text cannot be changed into a variable representing a number). Variables are created as follows:
```java
public class Main {
public static void main(String[] args) {
System.out.println("Hello Tutorial World!");
System.out.println("This is another line of text!");
String someText;
int someNumber;
someText = "Some Text";
someNumber = 10;
}
}
```
As you can see from line 6 and 7, to declare a variable, you need to put the variable's type first (`String` meaning text, `int` meaning an integer; a number without a decimal point), followed by the variable's name, which you can choose yourself.
For now, these variables don't have a value assigned to them yet. That's done in line 9 and 10. To assign a value to a variable, you first put the variable's name, followed by an equals sign `=`, followed by your desired value. For numbers, you can simply put them down, but text has to be wrapped in quotes `""` (which is also why the text in lines 3 and 4 is wrapped in quotes).
To actually do something with the variables, let's have them be printed out to the console. *I've omitted the rest of the code here because it's getting quite long, but I'm just adding the following lines inside the inner braces `{}` below the already written code.*
```java
System.out.println(someText);
System.out.println(someNumber);
```
As you can see, to print a variable's value to the console, all you have to do is put its name where you would usually put a piece of text directly.
# Variable Manipulation
Obviously, this isn't really that exciting yet: Our program isn't really doing anything so far. Let's manipulate our variables. I've added the following code:
```java
someNumber = someNumber + 5;
System.out.println(someNumber);
```
As you can see, you can use a plus sign `+` (as well as other mathematical operators) to do math with numbers. The first statement changes `someNumber`'s value to a new value: `someNumber + 5`. Running the program will show you that the result is pretty obvious: 15 is printed, because 10 + 5 = 15.
The same kind of manipulation can be done with text, which is especially useful for printing variables to the console. The plus sign `+` chains two strings of text together.
```java
String chainedText = "The resulting value is " + someNumber;
System.out.println(chainedText);
```
Here, a new variable is created (`String chainedText`) and its value is set (`=`) to the shown string plus the value of the `someNumber` variable. When added to the rest of the code, running this code should display `The resulting value is 15`.
# Conclusion
Now obviously, this is just the beginning of what you can learn and do with Java. I feel like this is a good point to finish the first part of this tutorial. I know it's probably not super interesting yet, but bear with me for a little while and you'll pretty quickly be able to do some pretty cool stuff.
Before you click away, I'd like to ask you some questions that you can answer by clicking on the discussion link below this post:
- Could you follow this tutorial?
- What do you think about the pacing? Is it too fast or too slow, are there too many examples or too few?
- Do you want me to actually continue this tutorial series?
Anyway, thanks a lot for reading and happy coding! <3
***
[^1]: Also, it's what Minecraft mods are written in, which is probably the reason most of you are here.
[^2]: Compiling is what occurs when code written by you is converted into something that the computer can actually understand. More on that later, probably.

View file

@ -1,170 +1,163 @@
---
layout: blog
title: "☕ Java Tutorial, Part 2: Intro to Conditions and Loops"
description: The second part of my post series for programming beginners. This one is all about conditions and loops.
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1182354544707198976
archived: true
---
If you're reading this, then I assume you have already read the first part of this tutorial series, in which we covered setting up a program, making it print "Hello World" to the console, as well as how to declare and set the values of variables.
Today, we'll be talking about conditions and loops.[^1] As with the last tutorial, I'll be writing most of the code snippets first and then explaining what exactly they mean and do right after.
# The `if` condition
Let's say you want to create a program that checks if a given number `i` is greater than or equal to 27. Well, you do it like this:
```java
public class Main {
public static void main(String[] args) {
// This is where the code from the first tutorial would be
// if I hadn't deleted it for visual clarity
// Also, side note: You can use "//" to create comments:
// lines that aren't interpreted as code.
int i = 15;
if (i >= 27) {
System.out.println("i is greater than or equal to 27!");
}
}
}
```
So what we see here is called the `if` condition. It's structured as follows: You write the word `if`, followed by *the condition*, which is wrapped in parentheses `()`. You can then open curly braces `{}`, and any instructions that are written between them will only be executed if your supplied condition holds true.
The condition can be any statement that can either be `true` (correct) or `false` (incorrect). In this case, we're comparing two numbers with each other; there are several other ways to compare two numbers: `>`, `<`, `>=`, `<=`, `!=` and `==`, where the last two mean "not equal" and "exactly equal".[^2]
An important thing to note at this point is that this behavior is *different* with `String` variables.[^3] Comparing if two strings are equal by using `==` will not result in the behavior you might expect. Instead, you should compare two strings using `equals()` as follows:
```java
String s1 = "This is some text";
String s2 = "This is also some text";
if (s1.equals(s2)) {
System.out.println("s1 and s2 are equal!");
}
```
## The `boolean` type
A condition like this (which can either be `true` or `false`) can also be stored in a `boolean` variable[^4] whose state can then be checked using a similar `if` statement:
```java
int i = 15;
boolean i27 = i >= 27;
if (i27) {
System.out.println("i is greater than or equal to 27!");
}
```
The above code has the same effect as the code we looked at before. The cool thing about this method is that you can easily check if a condition *doesn't* hold true by simply adding an exclamation point `!` in front of any boolean variable:
```java
if (!i27) {
System.out.println("i is NOT greater than or equal to 27!");
}
// As you can see, this is especially useful when comparing strings
String s1 = "This is some text";
String s2 = "This is also some text";
if (!s1.equals(s2)) {
System.out.println("s1 and s2 are NOT equal!");
}
```
Additionally, two boolean variables (or simply two conditions) can be chained together in two ways:
```java
int i = 15;
if (i == 12 || i == 13) {
System.out.println("i is either 12 or 13!");
}
if (i >= 10 && i < 20) {
System.out.println("i is somewhere between 10 and 20");
}
```
As you can see in lines 2 to 4, two pipes `||` represent the `OR` operator: A combined condition using it holds true if the left side is true, or the right side is true, or both sides are true.
As you can see in lines 5 to 7, two ampersands `&&` represent the `AND` operator: A combined condition using it holds true if *both* sides are also true.
You can also wrap your checks in parentheses `()` to create a more complex condition, similarly to how you would use them in a mathematical expression:
```java
int j = 10;
int k = 15;
boolean combination = (j == 12 && k == 13) || (j == 10 && k == 25);
System.out.println(combination);
```
The above code will cause `true` to be printed if `j` equals 12 and `k` equals 13 *or* if `j` equals 10 and `k` equals 25. Otherwise, `false` will be printed.
## `else`
If you want something *else* to happen if a certain condition doesn't hold true, then you can do something like the following:
```java
int i = 15;
if (i >= 27) {
System.out.println("i is greater than or equal to 27!");
} else {
System.out.println("i is less than 27 :(");
}
```
As you can see, writing `else` after any `if` statement causes the code contained in the following curly braces `{}` to only be executed if the `if` statement's condition *isn't* true.
Optionally, you can also combine the `else` with another `if` statement to check for a different condition. You can do this as many times as you want:
```java
int i = 15;
if (i >= 27) {
System.out.println("i is greater than or equal to 27!");
} else if (i <= 10) {
System.out.println("i is less than 27, but also less than or equal to 10");
} else {
System.out.println("i is somewhere between 11 and 26");
}
```
# The `for` loop
The `if` condition is already pretty powerful, because you can execute different code based on different situations. Another useful thing is the `for` loop. How it's written and what it does can be a bit complicated to understand at first, but I'll try to break the following example down for you:
```java
for (int index = 0; index < 3; index = index + 1) {
System.out.println("The current index is " + index);
}
```
This specific `for` loop causes the following to be printed to the console:
```
The current index is 0
The current index is 1
The current index is 2
```
As you might be able to tell from that, a `for` loop causes any number of instructions in it to be executed a certain amount of times.
Its structure is pretty similar to the `if` statement's: First, you write the word `for`, followed by some loop instructions inside parentheses `()`, and then you open curly braces `{}` which contain the instructions that should be executed multiple times. The loop instructions contain three parts, which are separated by semicolons `;`:
- `int index = 0;` is the declaration of the *loop variable*: This is the instruction that will be executed *before* the loop starts running.
- `index < 3;` is the *loop's condition*: Before every time the loop's content is run, a check is done to make sure that this condition is still true. If it's not true anymore, the loop stops running.
- `index = index + 1` is the instruction that is executed *after* every time the loop's content has finished running.
So in the case of our loop, the following things happen:
- The `index` variable is declared and set to 0.
- `index < 3` is checked, which is obviously true.
- The loop's content is run, which causes the print to the console.
- `index = index + 1` is executed, which sets `index` to 1.
- `index < 3` is checked, which is still true.
- The loop's content is run again, and so on.
## `break`
If you want to exit a `for` loop before its condition turns to `false`, you can use the `break;` statement as follows:
```java
for (int index = 0; index < 10000; index = index + 1) {
System.out.println("The current index is " + index);
if (index >= 2) {
break;
}
}
```
This loop has the same effect as the one we looked at before. Despite the loop condition specifying that it should repeat 10000 times, it is only repeated three times, because the condition on line 4 is evaluated every time and the loop is forced to stop running once it is true.
# Conclusion
In this tutorial, you (hopefully) learned what the `if` and `for` statements are and how to use them. By now, you should be able to write some more complicated code. As a little exercise, you can try to solve the following problem using the things you learned in the last two tutorials:
> Given a variable `i` and another variable `exp`, calculate if the result of `i`<sup>`exp`</sup> (`i` to the power of `exp`) is less than or equal to the value of another variable `j` (and print the result to the console accordingly).
If you're stuck, you can check out my solution [here](https://gist.github.com/Ellpeck/999bafe352f84d5e1f09750e026d5bbf).
Thanks for reading this tutorial and I hope it helped you out! If you have any feedback or questions about it, you can click the discussion link below and ask me on Twitter, or join my Discord server using the widget on the main page. Happy coding!
[^1]: I'm covering this topic *before* I cover what exactly classes and methods are. I think that conditions and loops take importance here, because they're used broadly in every language as well as most programs, whereas object orientation is a feature specific to some languages, as well as specific to more "complex" programs. It's also somewhat complicated, and I want to explain it right, because when I learned Java, I didn't even remotely understand it correctly at first.
[^2]: Note that, when *comparing* two numbers, two equals signs `==` are used. This is different from *assigning a value* to a variable, which only uses one equals sign `=`.
[^3]: Why exactly this is the case will be discussed later when we get into object orientation. The different behavior mentioned here is the case for any variable types which aren't so-called *native types*. Any variable whose type starts with an uppercase letter is not a native type, because it derives from a *class*.
[^4]: Named after [George Boole](https://en.wikipedia.org/wiki/George_Boole), a mathematician.
If you're reading this, then I assume you have already read the first part of this tutorial series, in which we covered setting up a program, making it print "Hello World" to the console, as well as how to declare and set the values of variables.
Today, we'll be talking about conditions and loops.[^1] As with the last tutorial, I'll be writing most of the code snippets first and then explaining what exactly they mean and do right after.
# The `if` condition
Let's say you want to create a program that checks if a given number `i` is greater than or equal to 27. Well, you do it like this:
```java
public class Main {
public static void main(String[] args) {
// This is where the code from the first tutorial would be
// if I hadn't deleted it for visual clarity
// Also, side note: You can use "//" to create comments:
// lines that aren't interpreted as code.
int i = 15;
if (i >= 27) {
System.out.println("i is greater than or equal to 27!");
}
}
}
```
So what we see here is called the `if` condition. It's structured as follows: You write the word `if`, followed by *the condition*, which is wrapped in parentheses `()`. You can then open curly braces `{}`, and any instructions that are written between them will only be executed if your supplied condition holds true.
The condition can be any statement that can either be `true` (correct) or `false` (incorrect). In this case, we're comparing two numbers with each other; there are several other ways to compare two numbers: `>`, `<`, `>=`, `<=`, `!=` and `==`, where the last two mean "not equal" and "exactly equal".[^2]
An important thing to note at this point is that this behavior is *different* with `String` variables.[^3] Comparing if two strings are equal by using `==` will not result in the behavior you might expect. Instead, you should compare two strings using `equals()` as follows:
```java
String s1 = "This is some text";
String s2 = "This is also some text";
if (s1.equals(s2)) {
System.out.println("s1 and s2 are equal!");
}
```
## The `boolean` type
A condition like this (which can either be `true` or `false`) can also be stored in a `boolean` variable[^4] whose state can then be checked using a similar `if` statement:
```java
int i = 15;
boolean i27 = i >= 27;
if (i27) {
System.out.println("i is greater than or equal to 27!");
}
```
The above code has the same effect as the code we looked at before. The cool thing about this method is that you can easily check if a condition *doesn't* hold true by simply adding an exclamation point `!` in front of any boolean variable:
```java
if (!i27) {
System.out.println("i is NOT greater than or equal to 27!");
}
// As you can see, this is especially useful when comparing strings
String s1 = "This is some text";
String s2 = "This is also some text";
if (!s1.equals(s2)) {
System.out.println("s1 and s2 are NOT equal!");
}
```
Additionally, two boolean variables (or simply two conditions) can be chained together in two ways:
```java
int i = 15;
if (i == 12 || i == 13) {
System.out.println("i is either 12 or 13!");
}
if (i >= 10 && i < 20) {
System.out.println("i is somewhere between 10 and 20");
}
```
As you can see in lines 2 to 4, two pipes `||` represent the `OR` operator: A combined condition using it holds true if the left side is true, or the right side is true, or both sides are true.
As you can see in lines 5 to 7, two ampersands `&&` represent the `AND` operator: A combined condition using it holds true if *both* sides are also true.
You can also wrap your checks in parentheses `()` to create a more complex condition, similarly to how you would use them in a mathematical expression:
```java
int j = 10;
int k = 15;
boolean combination = (j == 12 && k == 13) || (j == 10 && k == 25);
System.out.println(combination);
```
The above code will cause `true` to be printed if `j` equals 12 and `k` equals 13 *or* if `j` equals 10 and `k` equals 25. Otherwise, `false` will be printed.
## `else`
If you want something *else* to happen if a certain condition doesn't hold true, then you can do something like the following:
```java
int i = 15;
if (i >= 27) {
System.out.println("i is greater than or equal to 27!");
} else {
System.out.println("i is less than 27 :(");
}
```
As you can see, writing `else` after any `if` statement causes the code contained in the following curly braces `{}` to only be executed if the `if` statement's condition *isn't* true.
Optionally, you can also combine the `else` with another `if` statement to check for a different condition. You can do this as many times as you want:
```java
int i = 15;
if (i >= 27) {
System.out.println("i is greater than or equal to 27!");
} else if (i <= 10) {
System.out.println("i is less than 27, but also less than or equal to 10");
} else {
System.out.println("i is somewhere between 11 and 26");
}
```
# The `for` loop
The `if` condition is already pretty powerful, because you can execute different code based on different situations. Another useful thing is the `for` loop. How it's written and what it does can be a bit complicated to understand at first, but I'll try to break the following example down for you:
```java
for (int index = 0; index < 3; index = index + 1) {
System.out.println("The current index is " + index);
}
```
This specific `for` loop causes the following to be printed to the console:
```
The current index is 0
The current index is 1
The current index is 2
```
As you might be able to tell from that, a `for` loop causes any number of instructions in it to be executed a certain amount of times.
Its structure is pretty similar to the `if` statement's: First, you write the word `for`, followed by some loop instructions inside parentheses `()`, and then you open curly braces `{}` which contain the instructions that should be executed multiple times. The loop instructions contain three parts, which are separated by semicolons `;`:
- `int index = 0;` is the declaration of the *loop variable*: This is the instruction that will be executed *before* the loop starts running.
- `index < 3;` is the *loop's condition*: Before every time the loop's content is run, a check is done to make sure that this condition is still true. If it's not true anymore, the loop stops running.
- `index = index + 1` is the instruction that is executed *after* every time the loop's content has finished running.
So in the case of our loop, the following things happen:
- The `index` variable is declared and set to 0.
- `index < 3` is checked, which is obviously true.
- The loop's content is run, which causes the print to the console.
- `index = index + 1` is executed, which sets `index` to 1.
- `index < 3` is checked, which is still true.
- The loop's content is run again, and so on.
## `break`
If you want to exit a `for` loop before its condition turns to `false`, you can use the `break;` statement as follows:
```java
for (int index = 0; index < 10000; index = index + 1) {
System.out.println("The current index is " + index);
if (index >= 2) {
break;
}
}
```
This loop has the same effect as the one we looked at before. Despite the loop condition specifying that it should repeat 10000 times, it is only repeated three times, because the condition on line 4 is evaluated every time and the loop is forced to stop running once it is true.
# Conclusion
In this tutorial, you (hopefully) learned what the `if` and `for` statements are and how to use them. By now, you should be able to write some more complicated code. As a little exercise, you can try to solve the following problem using the things you learned in the last two tutorials:
> Given a variable `i` and another variable `exp`, calculate if the result of `i`<sup>`exp`</sup> (`i` to the power of `exp`) is less than or equal to the value of another variable `j` (and print the result to the console accordingly).
If you're stuck, you can check out my solution [here](https://gist.github.com/Ellpeck/999bafe352f84d5e1f09750e026d5bbf).
Thanks for reading this tutorial and I hope it helped you out! If you have any feedback or questions about it, you can click the discussion link below and ask me on Twitter, or join my Discord server using the widget on the main page. Happy coding!
***
[^1]: I'm covering this topic *before* I cover what exactly classes and methods are. I think that conditions and loops take importance here, because they're used broadly in every language as well as most programs, whereas object orientation is a feature specific to some languages, as well as specific to more "complex" programs. It's also somewhat complicated, and I want to explain it right, because when I learned Java, I didn't even remotely understand it correctly at first.
[^2]: Note that, when *comparing* two numbers, two equals signs `==` are used. This is different from *assigning a value* to a variable, which only uses one equals sign `=`.
[^3]: Why exactly this is the case will be discussed later when we get into object orientation. The different behavior mentioned here is the case for any variable types which aren't so-called *native types*. Any variable whose type starts with an uppercase letter is not a native type, because it derives from a *class*.
[^4]: Named after [George Boole](https://en.wikipedia.org/wiki/George_Boole), a mathematician.

View file

@ -1,184 +1,175 @@
---
layout: blog
title: "☕ Java Tutorial, Part 3: (Static) Methods"
description: In this Java tutorial for beginners, we cover what (static) methods, parameters and return types are.
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1182775985885847558
archived: true
---
I've been thinking about how to structure this tutorial a lot, and I decided to teach you all about methods *before* I get into object orientation, so you'll have to wait a little while longer before we get into the real nitty-gritty. To understand this one, you should've already read and understood tutorials 1 and 2. If you haven't, you can get to them by clicking the "Previous Post" button in the navigation bar.
So, here goes!
# Methods
Methods are a way, in programming, to move certain code into a different location where it's more organized and, more importantly, where it can be called multiple times from multiple locations, possibly with different data. You'll see what exactly that means in a minute.
Let's create a simple method in our main class:
```java
public class Main {
public static void main(String[] args) {
// I've omitted the code from the previous tutorials for readability
}
public static void printInfo() {
System.out.println("This is some important information!");
}
}
```
Let's take a look at the `printInfo` method I created. In this tutorial, we'll only be talking about `public` and `static` methods, so you'll just have to take that part for granted for now. Following that, you write `void` and then the name of your method (which can be anything you want), followed by parentheses `()` and then braces `{}`. Similarly to the `if` statement and the `for` loop, the method's content goes between those two curly braces, as I have done in this example with the print statement.
As you can see, all a method is is pretty much a collection of code. At this point, you might've already noticed that that is exactly what the `main` structure we've previously been writing all of our code into is: `main` is just another method.
To *call* a method, that is to have the code inside of it be executed, all you have to do is write the following:
```java
public class Main {
public static void main(String[] args) {
// I've omitted the code from the previous tutorials for readability
printInfo();
}
public static void printInfo() {
System.out.println("This is some important information!");
}
}
```
There you go.
## Variables
It should be noted at this point that variables which are declared *inside* of a method, like the ones we've been using in the first two tutorials, are known as `local variables`. What this means is that they only exist *inside* of the method they are declared in. Check out this example:
```java
// This method declares a variable i that is set to 0
public static void methodOne() {
int i = 0;
}
// This method declares a *different* variable i that is set to 1
public static void methodTwo() {
int i = 1;
}
// This method will cause an error if you paste it into your IDE:
// You cannot declare two variables with the same name in one method.
public static void erroringMethod() {
int i = 0;
int i = 1;
}
```
This same behavior also counts for `if`, `for` and any other curly braces `{}` that you see: They close off any variables which are created inside of them and make them available to only that location.
# Parameters
Now you might be wondering what the point of methods is if all they do is execute a predefined list of code. Well... you can actually make a method *accept* a set of data that they can use to do their processing. The data given to a method is called *parameters*, and these parameters are simply variables that you declare between the method's parentheses `()` right after its name.
Let's take the following example:
```java
public static void printInfo(String strg) {
System.out.println("This is some important information about " + strg);
}
```
I modified the method by adding the parameter `strg` to it, which is of the type `String`. What this means is that now, when calling the method, it expects you to give it a string that it can use to do stuff with (in this case, print it out).
If you add that parameter to the code we previously wrote, you might notice that your IDE is now displaying an error to you. You can't just do this anymore:
```java
public static void main(String[] args) {
printInfo();
}
```
As we just said, `printInfo` now wants us to give it a string whenever we call it. To give it that string, simply put it between the parentheses `()` of your method call like this:
```java
String someString = "Some String";
printInfo(someString);
// or, optionally, the shorter form:
printInfo("Some String");
```
Running your code now should cause `This is some important information about Some String` to be displayed in your console.
In case you're thinking "I've seen that somewhere before", then you would be correct: This is exactly the same thing you do when you try to print something out to the console: You call the `println` method and give it the text you want to print out as the parameter.
The cool thing is that a method can also accept *multiple* parameters, so multiple bits of data can be passed into it when calling it. Let's look at this example:
```java
public class Main {
public static void main(String[] args) {
printInfo("Some String", 5);
}
public static void printInfo(String strg, int amount) {
for (int i = 0; i < amount; i = i + 1) {
System.out.println("This is some important information about " + strg);
}
}
}
```
As you can see, the `printInfo` method now takes two parameters, separated by a comma `,`. The code inside of the method should already be familiar to you: It's a simple for loop that prints the same message `amount` times.
So now, we have something that also demonstrates pretty well the versatility of methods: We can now call `printInfo` with any information that will be printed any amount of times:
```java
printInfo("Some String", 5);
printInfo("Some other String", 10);
```
# Returning
Another thing that methods can do that is really useful is the ability to *return* certain data. What this means is that they can, after their execution finishes, take the data they created or modified and give it back to the caller:
```java
public class Main {
public static void main(String[] args) {
int tenSquared = square(10);
System.out.println(tenSquared); // prints 100
System.out.println(square(5)); // prints 25
}
public static int square(int i) {
return i * i;
}
}
```
As you can see, I created a method `square` that returns its parameter `i`, but squared. To make a method return a value, two things have to be done:
- The method's *return type* has to be declared. For methods that don't return anything, the return type is `void`. For methods that return something, just replace `void` with the *type of variable* that it returns.
- To actually return a specific value after it has been calculated, just type `return`, followed by the value and a semicolon `;`.
Now, this method can be used as if it were just another number: We can set variables to it and print it out. But instead of it being just another number, it actually executes the code inside it every time.
## Stopping execution
A thing that should be noted about returning is that any code that comes after a return statement will *not* be executed. In other words: After returning a value, a method will stop its execution, no matter what comes next.
```java
public static int square(int i) {
return i * i;
System.out.println("This will never be called");
}
```
In this specific case, that isn't really useful - why write code that can never be executed? But take this other example:
```java
public static boolean isIGreaterThanJ(int i, int j) {
if (i > j) {
return true;
}
System.out.println("i is NOT greater than j!");
return false;
}
```
In this example, the print statement will not be executed if `i` is greater than `j` despite the fact that it's not wrapped in an `else`, because the method gets returned out of before it can be called.
## Requirements
If you create a method that has a return type other than `void` (which, again, means "this method doesn't return anything"), then every possible path that the execution can take inside the method *needs* to return *something*.
What this means is that you can't have something like this:
```java
public static boolean isIGreaterThanJ(int i, int j) {
if (i > j) {
return true;
}
System.out.println("i is NOT greater than j!");
}
```
Because when `i` is *not* greater than `j`, the print statement will be executed but then, the method doesn't know what value to return, so this code will give out an error before it even tries to execute.
# Conclusion
So yea, that's pretty much everything important there is to know about (static) methods. In one of the next tutorials, we'll finally get into actual object orientation and we'll soon be taking a look at non-static methods, which are even more useful than static methods.
As a little exercise, you might want to create some methods with different return types and different parameters and call them. Have some fun!
I hope you enjoyed reading, and of course: Happy coding!
I've been thinking about how to structure this tutorial a lot, and I decided to teach you all about methods *before* I get into object orientation, so you'll have to wait a little while longer before we get into the real nitty-gritty. To understand this one, you should've already read and understood tutorials 1 and 2. If you haven't, you can get to them by clicking the "Previous Post" button in the navigation bar.
So, here goes!
# Methods
Methods are a way, in programming, to move certain code into a different location where it's more organized and, more importantly, where it can be called multiple times from multiple locations, possibly with different data. You'll see what exactly that means in a minute.
Let's create a simple method in our main class:
```java
public class Main {
public static void main(String[] args) {
// I've omitted the code from the previous tutorials for readability
}
public static void printInfo() {
System.out.println("This is some important information!");
}
}
```
Let's take a look at the `printInfo` method I created. In this tutorial, we'll only be talking about `public` and `static` methods, so you'll just have to take that part for granted for now. Following that, you write `void` and then the name of your method (which can be anything you want), followed by parentheses `()` and then braces `{}`. Similarly to the `if` statement and the `for` loop, the method's content goes between those two curly braces, as I have done in this example with the print statement.
As you can see, all a method is is pretty much a collection of code. At this point, you might've already noticed that that is exactly what the `main` structure we've previously been writing all of our code into is: `main` is just another method.
To *call* a method, that is to have the code inside of it be executed, all you have to do is write the following:
```java
public class Main {
public static void main(String[] args) {
// I've omitted the code from the previous tutorials for readability
printInfo();
}
public static void printInfo() {
System.out.println("This is some important information!");
}
}
```
There you go.
## Variables
It should be noted at this point that variables which are declared *inside* of a method, like the ones we've been using in the first two tutorials, are known as `local variables`. What this means is that they only exist *inside* of the method they are declared in. Check out this example:
```java
// This method declares a variable i that is set to 0
public static void methodOne() {
int i = 0;
}
// This method declares a *different* variable i that is set to 1
public static void methodTwo() {
int i = 1;
}
// This method will cause an error if you paste it into your IDE:
// You cannot declare two variables with the same name in one method.
public static void erroringMethod() {
int i = 0;
int i = 1;
}
```
This same behavior also counts for `if`, `for` and any other curly braces `{}` that you see: They close off any variables which are created inside of them and make them available to only that location.
# Parameters
Now you might be wondering what the point of methods is if all they do is execute a predefined list of code. Well... you can actually make a method *accept* a set of data that they can use to do their processing. The data given to a method is called *parameters*, and these parameters are simply variables that you declare between the method's parentheses `()` right after its name.
Let's take the following example:
```java
public static void printInfo(String strg) {
System.out.println("This is some important information about " + strg);
}
```
I modified the method by adding the parameter `strg` to it, which is of the type `String`. What this means is that now, when calling the method, it expects you to give it a string that it can use to do stuff with (in this case, print it out).
If you add that parameter to the code we previously wrote, you might notice that your IDE is now displaying an error to you. You can't just do this anymore:
```java
public static void main(String[] args) {
printInfo();
}
```
As we just said, `printInfo` now wants us to give it a string whenever we call it. To give it that string, simply put it between the parentheses `()` of your method call like this:
```java
String someString = "Some String";
printInfo(someString);
// or, optionally, the shorter form:
printInfo("Some String");
```
Running your code now should cause `This is some important information about Some String` to be displayed in your console.
In case you're thinking "I've seen that somewhere before", then you would be correct: This is exactly the same thing you do when you try to print something out to the console: You call the `println` method and give it the text you want to print out as the parameter.
The cool thing is that a method can also accept *multiple* parameters, so multiple bits of data can be passed into it when calling it. Let's look at this example:
```java
public class Main {
public static void main(String[] args) {
printInfo("Some String", 5);
}
public static void printInfo(String strg, int amount) {
for (int i = 0; i < amount; i = i + 1) {
System.out.println("This is some important information about " + strg);
}
}
}
```
As you can see, the `printInfo` method now takes two parameters, separated by a comma `,`. The code inside of the method should already be familiar to you: It's a simple for loop that prints the same message `amount` times.
So now, we have something that also demonstrates pretty well the versatility of methods: We can now call `printInfo` with any information that will be printed any amount of times:
```java
printInfo("Some String", 5);
printInfo("Some other String", 10);
```
# Returning
Another thing that methods can do that is really useful is the ability to *return* certain data. What this means is that they can, after their execution finishes, take the data they created or modified and give it back to the caller:
```java
public class Main {
public static void main(String[] args) {
int tenSquared = square(10);
System.out.println(tenSquared); // prints 100
System.out.println(square(5)); // prints 25
}
public static int square(int i) {
return i * i;
}
}
```
As you can see, I created a method `square` that returns its parameter `i`, but squared. To make a method return a value, two things have to be done:
- The method's *return type* has to be declared. For methods that don't return anything, the return type is `void`. For methods that return something, just replace `void` with the *type of variable* that it returns.
- To actually return a specific value after it has been calculated, just type `return`, followed by the value and a semicolon `;`.
Now, this method can be used as if it were just another number: We can set variables to it and print it out. But instead of it being just another number, it actually executes the code inside it every time.
## Stopping execution
A thing that should be noted about returning is that any code that comes after a return statement will *not* be executed. In other words: After returning a value, a method will stop its execution, no matter what comes next.
```java
public static int square(int i) {
return i * i;
System.out.println("This will never be called");
}
```
In this specific case, that isn't really useful - why write code that can never be executed? But take this other example:
```java
public static boolean isIGreaterThanJ(int i, int j) {
if (i > j) {
return true;
}
System.out.println("i is NOT greater than j!");
return false;
}
```
In this example, the print statement will not be executed if `i` is greater than `j` despite the fact that it's not wrapped in an `else`, because the method gets returned out of before it can be called.
## Requirements
If you create a method that has a return type other than `void` (which, again, means "this method doesn't return anything"), then every possible path that the execution can take inside the method *needs* to return *something*.
What this means is that you can't have something like this:
```java
public static boolean isIGreaterThanJ(int i, int j) {
if (i > j) {
return true;
}
System.out.println("i is NOT greater than j!");
}
```
Because when `i` is *not* greater than `j`, the print statement will be executed but then, the method doesn't know what value to return, so this code will give out an error before it even tries to execute.
# Conclusion
So yea, that's pretty much everything important there is to know about (static) methods. In one of the next tutorials, we'll finally get into actual object orientation and we'll soon be taking a look at non-static methods, which are even more useful than static methods.
As a little exercise, you might want to create some methods with different return types and different parameters and call them. Have some fun!
I hope you enjoyed reading, and of course: Happy coding!

View file

@ -1,216 +1,209 @@
---
layout: blog
title: "☕ Java Tutorial, Part 4: Classes and Objects"
description: In this Java tutorial for beginners, we cover the basics of creating classes with a constructor, some fields and some methods and creating objects of them.
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1183857460660101133
archived: true
---
So you've gotten far enough into Java now that you'll finally be able to learn about what this "object orientation" jazz is that everyone keeps talking about. I feel like this is a somewhat complicated topic to explain, so bear with me.
# Classes
So far in the tutorial, we've only ever used classes as basic storage containers for variables and methods. Let's stick with that for another moment while we create a simple `Utility` class that has a method we might want to use throughout our program. We'll create it in a new file (Right-Click on `src`, `New`, `Java Class` in IntelliJ) like so:
```java
public class Utility {
}
```
As you can see, this structure is similar to that of our `Main` class: The word `public`, followed by the word `class`, followed by the name of the class (which, in 99% of cases, should match the file name), followed by curly braces `{}`, which, as you might expect by now, contain the content of our class.
So let's add a method to it that, I don't know, returns the absolute value of an integer passed to it. We'll call this one `abs`, for "absolute".[^1]
```java
public class Utility {
public static int abs(int num) {
if (num < 0) {
return -num;
} else {
return num;
}
}
}
```
By now, this code should be pretty easy for you to understand: We've added a method called `abs` that takes in an integer parameter called `num`, and if `num` is negative, then we return the negated value of num (`-num`).
Now, because this method is in a different class to the one we might want to use the method in (`Main` in our case), we have to explicitly mention the class the method is in by prefixing the class' name followed by a dot `.` when calling it, like so:
```java
public class Main {
public static void main(String[] args) {
// This is our usual main method
int abs = Utility.abs(-17);
System.out.println(abs);
}
}
```
# Objects
Let's finally move away from `static` methods and into object orientation.[^2] I'll give you a quick, made-by-me outline of what object orientation even *means*, based on an example that might be a bit easier for you to understand.
Let's imagine we're not talking about objects, but *tables*. When you say something like "A table has four legs", you're not talking about a *specific* table, but rather, you're talking about the *concept* of what a table is. In Java, this is what a class is. Now, when you say something like "This table has four legs" while pointing to the table on the other side of the room, then you're talking about a very specific table, not just a concept anymore. In Java, this is what an object is. When we now say "object oriented", we merely mean "code that makes use of objects."
Another way to think of it is that classes are *blueprints* for certain things, and objects are the actual constructions that were created by following those blueprints.
## Creating Objects
So let's stick with this table example for now. First, we'll create a class called `Table` like so:
```java
public class Table {
}
```
So now, we have a very basic blueprint of a table.
Now, let's go back into our main class and actually create a *specific* table like so:
```java
public class Main {
public static void main(String[] args) {
// This is our usual main method
Table myTable;
myTable = new Table();
}
}
```
As you can see, the structure of line 5 is the same as how we usually declare variables: `Table` is the variable type (this is where we usually put `int` or `String`), and `myTable` is the name of the variable. Line 6 is where the new stuff happens, however: We set the variable's *value* to a newly created *instance* of the `Table` class. This is us creating a specific table following the blueprint that is the `Table` class: We write `new`, then the class' name, and then opening and closing parentheses `()`.
# Fields
What we have so far isn't really that interesting yet - our table doesn't really have any properties yet. But now, we can actually add *fields* to our class: Certain variables that each object will share, but whose values can also be *different* for each object. Fields are declared like normal variables, but are placed at the top of a class, prefixed with the `public` keyword like so:
```java
public class Table {
public int legAmount;
}
```
Now, each table *instance* can have its amount of legs modified as well as displayed like so:
```java
Table myTable = new Table();
myTable.legAmount = 4;
System.out.println("My table has " + myTable.legAmount + " legs");
```
As you can see, to access an object's fields, all you have to do is write the variable name, followed by a dot `.`, followed by the field's name.
The important thing to know here is that this variable is now unique for *every table instance you create*, meaning the following code...
```java
Table myTable = new Table();
Table myOtherTable = new Table();
myTable.legAmount = 4;
myOtherTable.legAmount = 25;
System.out.println("My table has " + myTable.legAmount + " legs");
System.out.println("My other table has " + myOtherTable.legAmount + " legs");
```
...will print out `My table has 4 legs`, followed by `My other table has 25 legs`.
# Methods
Obviously, you already know what methods are, but we can finally get rid of the `static` keyword for those as well. Similarly to what we can do with fields, we can also add methods to classes that can do unique things based on the object they're being called for. Let's take a look at the following code:
```java
public class Table {
public int legAmount;
public void printLegAmount() {
System.out.println("I have a certain amount of legs");
}
}
```
As you can see, I've added a (non-static) method to this class called `printLegAmount`. Now we can change the code in our `Main` class to just call that method instead:
```java
Table myTable = new Table();
myTable.legAmount = 4;
myTable.printLegAmount();
```
## The `this` keyword
Now that specific example isn't really that useful yet, because every single table we create will print out `I have a certain amount of legs`, when it would really be nicer if the table printed out its amount of legs stored in the `legAmount` variable.
That's exactly what the `this` keyword is for.[^3] Let's modify our method from before:
```java
public class Table {
public int legAmount;
public void printLegAmount() {
System.out.println("I have " + this.legAmount + " legs");
}
}
```
As you can see, `this` is being used here similarly to how we were using `myTable.legAmount` earlier. That has a good reason: `this` also refers to an object. However, the object that `this` refers to is *the current one*. When calling a method like `printLegAmount` for a specific object (`myTable` in our case), the `this` keyword will always refer to that object.
So if we have code like this...
```java
Table myTable = new Table();
Table myOtherTable = new Table();
myTable.legAmount = 4;
myOtherTable.legAmount = 25;
myTable.printLegAmount();
myOtherTable.printLegAmount();
```
...then the result will be the same as before: `My table has 4 legs`, followed by `My other table has 25 legs`, because in `myTable.printLegAmount()`, `this` will be `myTable`, and in `myOtherTable.printLegAmount()`, `this` will be `myOtherTable`.
# Constructors
Constructors are a special kind of method in Java. You can't really execute them manually, but instead, they're automatically called every time a new instance is created (so every time you use the `new` keyword).
Let's look at the following example, which adds a constructor to our `Table` class that automatically sets the `legAmount` variable to 4.
```java
public class Table {
public int legAmount;
// The constructor
public Table() {
this.legAmount = 4;
}
public void printLegAmount() {
System.out.println("I have " + this.legAmount + " legs");
}
}
```
As you can see, a constructor is different from other methods in that it *doesn't have a name*, and that its return type is *the same as the class it is in*.
So now, any instance of `Table` that we create will automatically have a leg amount of 4:
```java
Table myTable = new Table();
myTable.printLegAmount(); // "I have 4 legs"
```
## Constructor parameters
Just like in any other method, we can add a set of variables that the constructor accepts (parameters). In our case, we could make a table request a `legAmount` when it's initialized as follows:
```java
// The constructor
public Table(int legs) {
this.legAmount = legs;
}
```
Now, when creating a new instance of `Table`, we have to actually give it a leg amount that it should start with:
```java
Table myTable = new Table(4);
```
# So what about `static`?
As you have learned throughout this tutorial, non-static methods and variables inside of classes have a key property: They're unique for every object that is created of that class. So why have we been using `static` throughout both our `Main` and our `Utility` classes?
The `static` keyword tells any field or method that it should *not* be unique for every object that is created. Even if we created an instance of our `Main` class (`new Main()`), for example, the `main` method would not be unique for that instance, because it is marked with the `static` keyword. This also means that using the `this` keyword would make no sense in a static method (which object would it reference?), which is why doing so doesn't work.
Similarly, if we made the `abs` method in our `Utility` class non-static, then we could *not* access it simply by typing `Utility.abs()`, because the method would be unique for every object that is created of the class. We *could* then use the `abs` method if we first created an instance of the `Utility` class (`new Utility()`).
# Conclusion
So yea, that's quite a lot of information for you to process. The introduction to objects is a pretty big step, but it also allows for a lot of cool new stuff that you can learn. In the next couple tutorials, we'll expand on the concept of objects and learn some more stuff that you can do with them to make them even more useful.
For now, though, I'll leave you with this little exercise you can do to facilitate what you learned about objects and to get a better understanding of how useful they are:[^4]
> Let's imagine you're managing a small car dealership. You want to have a way of managing all of the cars you have in stock. This includes keeping track of their brand names, their horsepower, their license plate texts and their mileage. Currently, you have four different cars in stock, and you want your program to print out all of the information about all of the cars.
If you're stuck, you can [get some hints](https://gist.github.com/Ellpeck/462022597659f554fdd75663359480d3) or [look at my solution](https://gist.github.com/Ellpeck/7a0f31306d05473c10e8bca1685510a4).
Next time, we'll be talking about arrays and lists.[^5] Happy coding!
[^1]: Java's default `Math` class already has an `abs` method that does this same thing, but for the sake of this tutorial, we'll ignore it (because I can't think of any other utility method to add, in all honesty).
[^2]: Note that I explain why we're omitting the `static` keyword from now on a bit later in this tutorial.
[^3]: In a lot of cases, using the `this` keyword is actually optional and it can just be left out altogether. However, I personally think that it increases code readability quite a bit and that it helps a lot with understanding how objects work, so I advise you to use it.
[^4]: This example is one that almost every instructor uses. It might be a bit boring and ridiculous, but it's simple and helps understanding the concept, so I also like using it when teaching people how to code.
[^5]: Note that there is also another huge topic to talk about when it comes to object orientation, which is *pointers* and how Java manages objects and their assignment to variables (including pass-by-reference and the like). I'll cover that soon. I promise. <3
So you've gotten far enough into Java now that you'll finally be able to learn about what this "object orientation" jazz is that everyone keeps talking about. I feel like this is a somewhat complicated topic to explain, so bear with me.
# Classes
So far in the tutorial, we've only ever used classes as basic storage containers for variables and methods. Let's stick with that for another moment while we create a simple `Utility` class that has a method we might want to use throughout our program. We'll create it in a new file (Right-Click on `src`, `New`, `Java Class` in IntelliJ) like so:
```java
public class Utility {
}
```
As you can see, this structure is similar to that of our `Main` class: The word `public`, followed by the word `class`, followed by the name of the class (which, in 99% of cases, should match the file name), followed by curly braces `{}`, which, as you might expect by now, contain the content of our class.
So let's add a method to it that, I don't know, returns the absolute value of an integer passed to it. We'll call this one `abs`, for "absolute".[^1]
```java
public class Utility {
public static int abs(int num) {
if (num < 0) {
return -num;
} else {
return num;
}
}
}
```
By now, this code should be pretty easy for you to understand: We've added a method called `abs` that takes in an integer parameter called `num`, and if `num` is negative, then we return the negated value of num (`-num`).
Now, because this method is in a different class to the one we might want to use the method in (`Main` in our case), we have to explicitly mention the class the method is in by prefixing the class' name followed by a dot `.` when calling it, like so:
```java
public class Main {
public static void main(String[] args) {
// This is our usual main method
int abs = Utility.abs(-17);
System.out.println(abs);
}
}
```
# Objects
Let's finally move away from `static` methods and into object orientation.[^2] I'll give you a quick, made-by-me outline of what object orientation even *means*, based on an example that might be a bit easier for you to understand.
Let's imagine we're not talking about objects, but *tables*. When you say something like "A table has four legs", you're not talking about a *specific* table, but rather, you're talking about the *concept* of what a table is. In Java, this is what a class is. Now, when you say something like "This table has four legs" while pointing to the table on the other side of the room, then you're talking about a very specific table, not just a concept anymore. In Java, this is what an object is. When we now say "object oriented", we merely mean "code that makes use of objects."
Another way to think of it is that classes are *blueprints* for certain things, and objects are the actual constructions that were created by following those blueprints.
## Creating Objects
So let's stick with this table example for now. First, we'll create a class called `Table` like so:
```java
public class Table {
}
```
So now, we have a very basic blueprint of a table.
Now, let's go back into our main class and actually create a *specific* table like so:
```java
public class Main {
public static void main(String[] args) {
// This is our usual main method
Table myTable;
myTable = new Table();
}
}
```
As you can see, the structure of line 5 is the same as how we usually declare variables: `Table` is the variable type (this is where we usually put `int` or `String`), and `myTable` is the name of the variable. Line 6 is where the new stuff happens, however: We set the variable's *value* to a newly created *instance* of the `Table` class. This is us creating a specific table following the blueprint that is the `Table` class: We write `new`, then the class' name, and then opening and closing parentheses `()`.
# Fields
What we have so far isn't really that interesting yet - our table doesn't really have any properties yet. But now, we can actually add *fields* to our class: Certain variables that each object will share, but whose values can also be *different* for each object. Fields are declared like normal variables, but are placed at the top of a class, prefixed with the `public` keyword like so:
```java
public class Table {
public int legAmount;
}
```
Now, each table *instance* can have its amount of legs modified as well as displayed like so:
```java
Table myTable = new Table();
myTable.legAmount = 4;
System.out.println("My table has " + myTable.legAmount + " legs");
```
As you can see, to access an object's fields, all you have to do is write the variable name, followed by a dot `.`, followed by the field's name.
The important thing to know here is that this variable is now unique for *every table instance you create*, meaning the following code...
```java
Table myTable = new Table();
Table myOtherTable = new Table();
myTable.legAmount = 4;
myOtherTable.legAmount = 25;
System.out.println("My table has " + myTable.legAmount + " legs");
System.out.println("My other table has " + myOtherTable.legAmount + " legs");
```
...will print out `My table has 4 legs`, followed by `My other table has 25 legs`.
# Methods
Obviously, you already know what methods are, but we can finally get rid of the `static` keyword for those as well. Similarly to what we can do with fields, we can also add methods to classes that can do unique things based on the object they're being called for. Let's take a look at the following code:
```java
public class Table {
public int legAmount;
public void printLegAmount() {
System.out.println("I have a certain amount of legs");
}
}
```
As you can see, I've added a (non-static) method to this class called `printLegAmount`. Now we can change the code in our `Main` class to just call that method instead:
```java
Table myTable = new Table();
myTable.legAmount = 4;
myTable.printLegAmount();
```
## The `this` keyword
Now that specific example isn't really that useful yet, because every single table we create will print out `I have a certain amount of legs`, when it would really be nicer if the table printed out its amount of legs stored in the `legAmount` variable.
That's exactly what the `this` keyword is for.[^3] Let's modify our method from before:
```java
public class Table {
public int legAmount;
public void printLegAmount() {
System.out.println("I have " + this.legAmount + " legs");
}
}
```
As you can see, `this` is being used here similarly to how we were using `myTable.legAmount` earlier. That has a good reason: `this` also refers to an object. However, the object that `this` refers to is *the current one*. When calling a method like `printLegAmount` for a specific object (`myTable` in our case), the `this` keyword will always refer to that object.
So if we have code like this...
```java
Table myTable = new Table();
Table myOtherTable = new Table();
myTable.legAmount = 4;
myOtherTable.legAmount = 25;
myTable.printLegAmount();
myOtherTable.printLegAmount();
```
...then the result will be the same as before: `My table has 4 legs`, followed by `My other table has 25 legs`, because in `myTable.printLegAmount()`, `this` will be `myTable`, and in `myOtherTable.printLegAmount()`, `this` will be `myOtherTable`.
# Constructors
Constructors are a special kind of method in Java. You can't really execute them manually, but instead, they're automatically called every time a new instance is created (so every time you use the `new` keyword).
Let's look at the following example, which adds a constructor to our `Table` class that automatically sets the `legAmount` variable to 4.
```java
public class Table {
public int legAmount;
// The constructor
public Table() {
this.legAmount = 4;
}
public void printLegAmount() {
System.out.println("I have " + this.legAmount + " legs");
}
}
```
As you can see, a constructor is different from other methods in that it *doesn't have a name*, and that its return type is *the same as the class it is in*.
So now, any instance of `Table` that we create will automatically have a leg amount of 4:
```java
Table myTable = new Table();
myTable.printLegAmount(); // "I have 4 legs"
```
## Constructor parameters
Just like in any other method, we can add a set of variables that the constructor accepts (parameters). In our case, we could make a table request a `legAmount` when it's initialized as follows:
```java
// The constructor
public Table(int legs) {
this.legAmount = legs;
}
```
Now, when creating a new instance of `Table`, we have to actually give it a leg amount that it should start with:
```java
Table myTable = new Table(4);
```
# So what about `static`?
As you have learned throughout this tutorial, non-static methods and variables inside of classes have a key property: They're unique for every object that is created of that class. So why have we been using `static` throughout both our `Main` and our `Utility` classes?
The `static` keyword tells any field or method that it should *not* be unique for every object that is created. Even if we created an instance of our `Main` class (`new Main()`), for example, the `main` method would not be unique for that instance, because it is marked with the `static` keyword. This also means that using the `this` keyword would make no sense in a static method (which object would it reference?), which is why doing so doesn't work.
Similarly, if we made the `abs` method in our `Utility` class non-static, then we could *not* access it simply by typing `Utility.abs()`, because the method would be unique for every object that is created of the class. We *could* then use the `abs` method if we first created an instance of the `Utility` class (`new Utility()`).
# Conclusion
So yea, that's quite a lot of information for you to process. The introduction to objects is a pretty big step, but it also allows for a lot of cool new stuff that you can learn. In the next couple tutorials, we'll expand on the concept of objects and learn some more stuff that you can do with them to make them even more useful.
For now, though, I'll leave you with this little exercise you can do to facilitate what you learned about objects and to get a better understanding of how useful they are:[^4]
> Let's imagine you're managing a small car dealership. You want to have a way of managing all of the cars you have in stock. This includes keeping track of their brand names, their horsepower, their license plate texts and their mileage. Currently, you have four different cars in stock, and you want your program to print out all of the information about all of the cars.
If you're stuck, you can [get some hints](https://gist.github.com/Ellpeck/462022597659f554fdd75663359480d3) or [look at my solution](https://gist.github.com/Ellpeck/7a0f31306d05473c10e8bca1685510a4).
Next time, we'll be talking about arrays and lists.[^5] Happy coding!
***
[^1]: Java's default `Math` class already has an `abs` method that does this same thing, but for the sake of this tutorial, we'll ignore it (because I can't think of any other utility method to add, in all honesty).
[^2]: Note that I explain why we're omitting the `static` keyword from now on a bit later in this tutorial.
[^3]: In a lot of cases, using the `this` keyword is actually optional and it can just be left out altogether. However, I personally think that it increases code readability quite a bit and that it helps a lot with understanding how objects work, so I advise you to use it.
[^4]: This example is one that almost every instructor uses. It might be a bit boring and ridiculous, but it's simple and helps understanding the concept, so I also like using it when teaching people how to code.
[^5]: Note that there is also another huge topic to talk about when it comes to object orientation, which is *pointers* and how Java manages objects and their assignment to variables (including pass-by-reference and the like). I'll cover that soon. I promise. <3

View file

@ -1,173 +1,166 @@
---
layout: blog
title: "☕ Java Tutorial, Part 5: Things I Left Out So Far"
description: In this Java tutorial for beginners, we cover some shorthands, some more data types, the difference between pass-by-reference and pass-by-value, null, as well as arrays and lists.
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1184894859133509632
archived: true
---
After that complicated stuff we did in the last tutorial, how about we take it down a notch in this one and talk about some additional things that I haven't mentioned this far, but that will still be very useful to you as a programmer. Most of these things won't really have much connection to each other, but I'll give you an example at the end of this tutorial that combines some of them into a single use case.
# Shorthands
Java has some shorthands to make addition and other mathematical operations faster. As there isn't really that much to explain (they're really just vocabulary), here's a list of them:
```java
// These two statements are equivalent
// (This also works with all other math operators)
i = i + 2;
i += 2;
// When adding or subtracting 1, you can just do this:
i++;
i--;
```
# Primitive Types
Primitive types (also called *native types*) are variable types in a language that are baked into the very foundation of the language. That means they don't derive from a class, but they just exist as part of the language itself. The two primitive types you already know are `int` (32 bit integers) and `boolean` (`true` and `false`). In Java, primitive types always start with a lowercase letter (instead of an uppercase letter for classes).
Along with `int` and `boolean`, there are some more primitive types that could come in useful when programming. Here are the ones I haven't mentioned yet, quickly summed up:
```java
// "char" represents a single character.
// Characters use single quotes ' rather than double quotes " (which are reserved for strings).
char character = 'A';
char fourthChar = "Hello World".charAt(4); // o
// "double" represents a 64bit floating point number
// which is a number with a decimal point
// It's called a double because it has double the precision of float (see below)
double d = 3.14;
// "float" represents a 32bit floating point number (less precise than double).
// To differentiate floats from doubles and ints, you have to append an "F".
float f = 12.5F;
// "long" represents a 64bit integer (which can store a lot higher numbers than int)
// To differentiate longs from ints, you have to append an "L".
long l = 9223372036854775807L;
// "short" represents a 16bit integer
short s = 32767;
// "byte" represents an 8bit integer
byte b = 127;
```
## What about `String`?
Oh boy. Strings are quite a mess in Java, because they *behave* like primitive types (more on that later), but they really *are* classes. Also, you can just initialize a string using quotes `""` instead of having to call `new String()`, which makes them behave differently than *any other class* in the language. This complication is also the reason that you can't just compare two strings using `==`, but you have to use `.equals()` instead. Meh.
## Pass-by-reference vs. pass-by-value
An important thing to know about primitive types compared to objects is the behavior they show when being passed into a method that accepts parameters. Let's look at the following code as an example:
```java
public class Main {
public static void main(String[] args) {
int i = 10;
addOne(i);
System.out.println(i); // still 10, what gives?
}
private static void addOne(int i){
i++;
}
}
```
As you can see, the `addOne` method adds one to the parameter passed into it. But despite that, the code in our `main` method still prints out the number 10. This happens because primitive types (and `String`, hurr durr) use what is called *pass-by-value*: When calling `addOne`, its `i` variable is just set to the *value* of the `i` variable from our `main` method. It doesn't know about the actual *variable* we pass in, just its *value*.
This behavior, however, is different when using Objects. Let's rewrite the code above to use a custom class that contains an `int` field:
```java
// Thing.java
public class Thing {
public int i;
public Thing(int i) {
this.i = i;
}
}
// Main.java
public class Main {
public static void main(String[] args) {
Thing thing = new Thing(10);
addOne(thing);
System.out.println(thing.i); // 11
}
private static void addOne(Thing thing) {
thing.i++;
}
}
```
As you can see, the program now prints out 11 instead of 10. That's because of something Java uses called *pointers*[^1]. Our `thing` variable in the `main` method doesn't really store the actual *instance*, but instead, it stores the location of that instance in memory. So when passing that variable into the `addOne` method, all we pass is the information "The thing we're trying to modify sits in this part of memory, so modify the data there, please." That's why, when we then change the `Thing`'s `i` variable, it's also modified outside of the method.
# `null`
Any variables that can store objects rather than primitive types (our `thing` variable from before, for example) can have a state where they don't point to an object, but instead, point to nothing. This "nothing" is called `null`.
```java
Thing noThing = null;
```
Using `null` can be useful in a lot of cases; however, it can also be quite dangerous: Trying to call a method or interact with a field of a variable that is `null` causes the program to crash:
```java
Thing noThing = null;
System.out.println(noThing.i); // crashes
```
Just like any other objects, you can use `null` in comparisons:
```java
if (noThing == null) {
System.out.println("I don't have a thing!");
}
```
# Arrays and Lists
Arrays and Lists are two data types that you'll be using a lot of, because they're super useful. Their names kind of already give away what they do, but in case they don't mean anything to you: Arrays and lists are two ways to store multiple pieces of data easily without having to manage each piece on its own. This can be useful for stuff like *students in a classroom*, where you won't really know for certain beforehand how many students will be there at any given day. The difference between Arrays and Lists is that the former has a *fixed size*, while the latter has a *dynamic size*.
## Arrays
Let's take a look at arrays first. An array has a fixed type, meaning that the objects in it can only be of the type you specify when creating the array. The variable type of an array of strings, for example, is written as `String[]`. To initialize an array, you have two options: Either you create an empty array based on a fixed size (where every slot will contain `null` to start with), or you create an array that already has stuff in it:
```java
int[] numbers = new int[10]; // empty array with 10 slots
String[] names = new String[]{"Justus", "Peter", "Bob"};
```
You can query and modify the data in an array using array brackets `[]` like so:
```java
String justus = names[0]; // get the 0th entry
names[1] = "Peter Shaw"; // modify the 1st entry
```
Note that, in Java, the first slot in an array has an index of 0, which means that the last slot of an array always has the index `length-1`. Every array has a `length` field which stores the amount of slots an array has, meaning you can query the length of `numbers` by writing `numbers.length`.
## Lists
Lists[^2] work much in the same way, with the main difference being the way you identify them. Lists use something called *generic types*[^3] to allow you to also give them a fixed type. The variable type of a list of strings, for example, is written as `ArrayList<String>`, using angle brackets `<>` (which are really just "greater than" and "less than" signs). To create a list, simply call its constructor using `new`:
```java
ArrayList<Integer> numbers = new ArrayList<>();
ArrayList<String> names = new ArrayList<>();
```
Note that, to create a list containing a primitive type, you need to specify its *wrapper class* instead of the native type itself[^4]. The wrapper class for `int` is `Integer`, and the wrapper classes for the other types are just their names with an uppercase first letter (`Boolean`, for example).
Also note that, when copying or writing this code in your IDE, it will automatically add `import` statements to the top of the class. All they do is make classes from other locations available to the current class, but you don't have to worry about them too much.
You can query and modify the data in a list using the methods from the `ArrayList` class:
```java
names.add("Justus"); // add entries
names.add("Peter");
names.add("Bob");
String justus = names.get(0); // get the 0th entry
names.set(1, "Peter Shaw"); // modify the 1st entry
// the length of a list can be read using the size() method
int length = names.size();
```
Note that, when trying to access an index of a list or an array that is either less than 0 or greater than or equal to its length, your program will crash, so be careful.
# Conclusion
So today, you learned some additional useful things about Java that I didn't really get a chance to mention at an earlier point. So let's write some code that makes use of them! If you want, you can try to solve the following problem, which is an extension of the problem from the previous tutorial:
> Let's imagine you're managing a small car dealership. You want to have a way of managing all of the cars you have in stock. This includes keeping track of their brand names, their horsepower, their license plate texts and their mileage. Currently, you have 15 parking spots for cars you can sell, 4 of which are already occupied with cars. Additionally, you keep track of a list of all the customers you have had so far (namely, you store their first and last names as well as the city they live in). At the current time, you've already had 3 customers. Additionally, you want to have a way of adding a new car to the first available parking spot, as well as a way to store the data of a new customer easily.
If you're stuck, you can [get some hints](https://gist.github.com/Ellpeck/3bdc69845d0d4c0e9511511dd06cfdc1) or [look at my solution](https://gist.github.com/Ellpeck/595b929ce666fca6fb0ed792ca98d71e).
Happy coding!
[^1]: Java's pointers work a lot differently from pointers in lower-level languages like C, because they're implicit: You don't create or manage them yourself. They're still called pointers though, so yea.
[^2]: Java has multiple types of lists (including, but not limited to `ArrayList` and `LinkedList`), but for the purpose of this tutorial, we'll only be looking at `ArrayList` since it's the most commonly used one and it also goes hand in hand with arrays.
[^3]: More on those in a later tutorial, probably. They're pretty useful.
[^4]: Which is also just another annoying property of Java that could've been implemented a lot better (like in C#), but oh well, it is what it is.
After that complicated stuff we did in the last tutorial, how about we take it down a notch in this one and talk about some additional things that I haven't mentioned this far, but that will still be very useful to you as a programmer. Most of these things won't really have much connection to each other, but I'll give you an example at the end of this tutorial that combines some of them into a single use case.
# Shorthands
Java has some shorthands to make addition and other mathematical operations faster. As there isn't really that much to explain (they're really just vocabulary), here's a list of them:
```java
// These two statements are equivalent
// (This also works with all other math operators)
i = i + 2;
i += 2;
// When adding or subtracting 1, you can just do this:
i++;
i--;
```
# Primitive Types
Primitive types (also called *native types*) are variable types in a language that are baked into the very foundation of the language. That means they don't derive from a class, but they just exist as part of the language itself. The two primitive types you already know are `int` (32 bit integers) and `boolean` (`true` and `false`). In Java, primitive types always start with a lowercase letter (instead of an uppercase letter for classes).
Along with `int` and `boolean`, there are some more primitive types that could come in useful when programming. Here are the ones I haven't mentioned yet, quickly summed up:
```java
// "char" represents a single character.
// Characters use single quotes ' rather than double quotes " (which are reserved for strings).
char character = 'A';
char fourthChar = "Hello World".charAt(4); // o
// "double" represents a 64bit floating point number
// which is a number with a decimal point
// It's called a double because it has double the precision of float (see below)
double d = 3.14;
// "float" represents a 32bit floating point number (less precise than double).
// To differentiate floats from doubles and ints, you have to append an "F".
float f = 12.5F;
// "long" represents a 64bit integer (which can store a lot higher numbers than int)
// To differentiate longs from ints, you have to append an "L".
long l = 9223372036854775807L;
// "short" represents a 16bit integer
short s = 32767;
// "byte" represents an 8bit integer
byte b = 127;
```
## What about `String`?
Oh boy. Strings are quite a mess in Java, because they *behave* like primitive types (more on that later), but they really *are* classes. Also, you can just initialize a string using quotes `""` instead of having to call `new String()`, which makes them behave differently than *any other class* in the language. This complication is also the reason that you can't just compare two strings using `==`, but you have to use `.equals()` instead. Meh.
## Pass-by-reference vs. pass-by-value
An important thing to know about primitive types compared to objects is the behavior they show when being passed into a method that accepts parameters. Let's look at the following code as an example:
```java
public class Main {
public static void main(String[] args) {
int i = 10;
addOne(i);
System.out.println(i); // still 10, what gives?
}
private static void addOne(int i){
i++;
}
}
```
As you can see, the `addOne` method adds one to the parameter passed into it. But despite that, the code in our `main` method still prints out the number 10. This happens because primitive types (and `String`, hurr durr) use what is called *pass-by-value*: When calling `addOne`, its `i` variable is just set to the *value* of the `i` variable from our `main` method. It doesn't know about the actual *variable* we pass in, just its *value*.
This behavior, however, is different when using Objects. Let's rewrite the code above to use a custom class that contains an `int` field:
```java
// Thing.java
public class Thing {
public int i;
public Thing(int i) {
this.i = i;
}
}
// Main.java
public class Main {
public static void main(String[] args) {
Thing thing = new Thing(10);
addOne(thing);
System.out.println(thing.i); // 11
}
private static void addOne(Thing thing) {
thing.i++;
}
}
```
As you can see, the program now prints out 11 instead of 10. That's because of something Java uses called *pointers*[^1]. Our `thing` variable in the `main` method doesn't really store the actual *instance*, but instead, it stores the location of that instance in memory. So when passing that variable into the `addOne` method, all we pass is the information "The thing we're trying to modify sits in this part of memory, so modify the data there, please." That's why, when we then change the `Thing`'s `i` variable, it's also modified outside of the method.
# `null`
Any variables that can store objects rather than primitive types (our `thing` variable from before, for example) can have a state where they don't point to an object, but instead, point to nothing. This "nothing" is called `null`.
```java
Thing noThing = null;
```
Using `null` can be useful in a lot of cases; however, it can also be quite dangerous: Trying to call a method or interact with a field of a variable that is `null` causes the program to crash:
```java
Thing noThing = null;
System.out.println(noThing.i); // crashes
```
Just like any other objects, you can use `null` in comparisons:
```java
if (noThing == null) {
System.out.println("I don't have a thing!");
}
```
# Arrays and Lists
Arrays and Lists are two data types that you'll be using a lot of, because they're super useful. Their names kind of already give away what they do, but in case they don't mean anything to you: Arrays and lists are two ways to store multiple pieces of data easily without having to manage each piece on its own. This can be useful for stuff like *students in a classroom*, where you won't really know for certain beforehand how many students will be there at any given day. The difference between Arrays and Lists is that the former has a *fixed size*, while the latter has a *dynamic size*.
## Arrays
Let's take a look at arrays first. An array has a fixed type, meaning that the objects in it can only be of the type you specify when creating the array. The variable type of an array of strings, for example, is written as `String[]`. To initialize an array, you have two options: Either you create an empty array based on a fixed size (where every slot will contain `null` to start with), or you create an array that already has stuff in it:
```java
int[] numbers = new int[10]; // empty array with 10 slots
String[] names = new String[]{"Justus", "Peter", "Bob"};
```
You can query and modify the data in an array using array brackets `[]` like so:
```java
String justus = names[0]; // get the 0th entry
names[1] = "Peter Shaw"; // modify the 1st entry
```
Note that, in Java, the first slot in an array has an index of 0, which means that the last slot of an array always has the index `length-1`. Every array has a `length` field which stores the amount of slots an array has, meaning you can query the length of `numbers` by writing `numbers.length`.
## Lists
Lists[^2] work much in the same way, with the main difference being the way you identify them. Lists use something called *generic types*[^3] to allow you to also give them a fixed type. The variable type of a list of strings, for example, is written as `ArrayList<String>`, using angle brackets `<>` (which are really just "greater than" and "less than" signs). To create a list, simply call its constructor using `new`:
```java
ArrayList<Integer> numbers = new ArrayList<>();
ArrayList<String> names = new ArrayList<>();
```
Note that, to create a list containing a primitive type, you need to specify its *wrapper class* instead of the native type itself[^4]. The wrapper class for `int` is `Integer`, and the wrapper classes for the other types are just their names with an uppercase first letter (`Boolean`, for example).
Also note that, when copying or writing this code in your IDE, it will automatically add `import` statements to the top of the class. All they do is make classes from other locations available to the current class, but you don't have to worry about them too much.
You can query and modify the data in a list using the methods from the `ArrayList` class:
```java
names.add("Justus"); // add entries
names.add("Peter");
names.add("Bob");
String justus = names.get(0); // get the 0th entry
names.set(1, "Peter Shaw"); // modify the 1st entry
// the length of a list can be read using the size() method
int length = names.size();
```
Note that, when trying to access an index of a list or an array that is either less than 0 or greater than or equal to its length, your program will crash, so be careful.
# Conclusion
So today, you learned some additional useful things about Java that I didn't really get a chance to mention at an earlier point. So let's write some code that makes use of them! If you want, you can try to solve the following problem, which is an extension of the problem from the previous tutorial:
> Let's imagine you're managing a small car dealership. You want to have a way of managing all of the cars you have in stock. This includes keeping track of their brand names, their horsepower, their license plate texts and their mileage. Currently, you have 15 parking spots for cars you can sell, 4 of which are already occupied with cars. Additionally, you keep track of a list of all the customers you have had so far (namely, you store their first and last names as well as the city they live in). At the current time, you've already had 3 customers. Additionally, you want to have a way of adding a new car to the first available parking spot, as well as a way to store the data of a new customer easily.
If you're stuck, you can [get some hints](https://gist.github.com/Ellpeck/3bdc69845d0d4c0e9511511dd06cfdc1) or [look at my solution](https://gist.github.com/Ellpeck/595b929ce666fca6fb0ed792ca98d71e).
Happy coding!
***
[^1]: Java's pointers work a lot differently from pointers in lower-level languages like C, because they're implicit: You don't create or manage them yourself. They're still called pointers though, so yea.
[^2]: Java has multiple types of lists (including, but not limited to `ArrayList` and `LinkedList`), but for the purpose of this tutorial, we'll only be looking at `ArrayList` since it's the most commonly used one and it also goes hand in hand with arrays.
[^3]: More on those in a later tutorial, probably. They're pretty useful.
[^4]: Which is also just another annoying property of Java that could've been implemented a lot better (like in C#), but oh well, it is what it is.

View file

@ -1,158 +1,151 @@
---
layout: blog
title: "☕ Java Tutorial, Part 6: Inheritance"
description: In this Java tutorial for beginners, we cover classes extending other classes and the instanceof keyword.
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1189904487722487809
archived: true
---
For this tutorial, let's expand on the car dealership example from the end of the last tutorial: You have a car dealership, and you want to expand it to sell various other types of motor vehicles; let's say... motorbikes and trucks as well. Obviously, these different kinds of vehicles all have different properties: A motorbike requires the use of a helmet, and an important piece of information about a truck might be how big its storage area is.
Now, this is where object-oriented languages like Java shine.
# Extending Other Classes
The concept of extending other classes is probably best explained with an example, so to start out with, let's create a class that represents a car with a certain amount of wheels:
```java
public class Car {
public int amountOfWheels;
public Car(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
}
```
As you can see, this is just a simple class with a variable that can store how many wheels a vehicle has.
Now, let's imagine we want to create a class that represents a truck, and we would want any truck to store information on how many wheels it has, as well as how many cubic meters of storage it has available:
```java
public class Truck {
public int amountOfWheels;
public int storageArea;
public Truck(int amountOfWheels, int storageArea) {
this.amountOfWheels = amountOfWheels;
this.storageArea = storageArea;
}
}
```
As you can see, just creating the class like usual isn't really that pretty, because now both our `Car` class and our `Truck` class have separate `amountOfWheels` fields, despite the fact that they really refer to the same thing: The amount of wheels of a vehicle.
What we'd like to be able to do instead is to have the amount of wheels stored in a different location that both our `Truck` and our `Car` class can access. The first step of achieving this is creating another class that only stores information both of our vehicles should have:
```java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
}
```
Now, we can use the `extends` keyword that Java provides to cause both of our other classes to also have access to this information. Let's modify our `Car` class first and I'll explain afterwards what exactly our modification will do.
```java
public class Car extends Vehicle {
public Car(int amountOfWheels) {
// Note that the line below will be explained in a bit
super(amountOfWheels);
}
}
```
As you can see, our `Car` class now `extends Vehicle`. This means that the `Car` class is basically just an *upgraded* version of the `Vehicle` class: It contains all of the information that the `Vehicle` class provides (including fields and methods), but it can also provide its own additional information. From the perspective of `Car`, `Vehicle` is the *superclass*, and from the perspective of `Vehicle`, `Car` is a *child class*.
It should be noted that any class can only extend *one* other class, so `Car` couldn't extend both `Vehicle` and, let's say, `FueledDevice`. However, the other way around is possible: `Vehicle` can be extended by multiple classes, meaning that we can modify our `Truck` class to also be a child class like this:
```java
public class Truck extends Vehicle {
public int storageArea;
public Truck(int amountOfWheels, int storageArea) {
super(amountOfWheels);
this.storageArea = storageArea;
}
}
```
As you can see, the `Truck` class now uses the `amountOfWheels` field from its superclass, but it defines its own `storageArea` field that only trucks have.
## `super`
Now, you've probably been wondering what line 5 means: `super(amountOfWheels)`. As we defined earlier, our `Vehicle` class takes one argument in its constructor: The amount of wheels that it has. As we also previously discussed, a constructor *has to be called* when a new object is created. So, when creating a `Truck` object, the `Truck` constructor is called, but, in turn, it needs to also implicitly call the `Vehicle` constructor.
When placing a `super()` call at the start of the constructor of a class, the constructor of the class that is being extended will be called. If that class takes a set of arguments in its constructor, we have to pass it those arguments, similarly to if we were creating an object of that class.
That's what our `super(amountOfWheels)` is doing here: It's calling the constructor of `Vehicle` with the `amountOfWheels` argument.
It should be noted that we don't *necessarily* have to pass a variable into the super constructor. For instance, we know for a fact that a truck will always have four wheels, so we can modify our `Truck` constructor to always specify 4 as the wheel amount given to the superclass:
```java
public Truck(int storageArea) {
super(4);
this.storageArea = storageArea;
}
```
## The `Object` class
It should be noted at this point that *all classes* extend the `Object` class implicitly (meaning you never have to write `extends Object` anywhere and it will still hold true). This will be useful pretty soon.
## About Variable Types
Let's imagine we want to store the stock that we sell at our dealership, without specifying separate lists for the different kinds of vehicles we're selling. What we can do is have that variable be of type `Vehicle`, which will automatically allow instances of child classes to be stored in the variable (or list) like so:
```java
ArrayList<Vehicle> stock = new ArrayList<>();
stock.add(new Car(4));
stock.add(new Truck(10));
// or in the case of variables
Vehicle car = new Car(4);
Vehicle truck = new Truck(10);
```
# `instanceof` and Type Casting
Staying with our list of the stock we have, let's imagine we want to find all of the *trucks* that we have in stock, disregarding any other kinds of vehicles that we might have.
This is what the `instanceof` keyword can be used for: Using it, you can ask any object if it derives of a certain class; that is, if any object is an instance of the specified class.
Let's create a method to return a list of all of the cars we have in stock:
```java
import java.util.ArrayList;
public class Main {
private static ArrayList<Vehicle> stock = new ArrayList<>();
public static void main(String[] args) {
stock.add(new Car(4));
stock.add(new Truck(10));
getTrucks();
}
private static void getTrucks() {
for (int i = 0; i < stock.size(); i++) {
Vehicle vehicle = stock.get(i);
if (vehicle instanceof Truck) {
System.out.println("Vehicle " + i + " is a truck!");
}
}
}
}
```
As you can see, I've expanded the code from before to have a `getTrucks` method, which iterates through every vehicle we have in stock and if it finds a vehicle that is an instance of the `Truck` class, it will print out a message.
Another way to think about the `instanceof` keyword is as a weird, clunky synonym to the word "is": When asking `vehicle instanceof Truck`, we're really asking "Is this vehicle a truck?"
Now that we know which of the vehicles we have in stock are trucks, it might also be useful to know the storage capabilities of all of those trucks. However, as you can see, the `i`th vehicle we're currently inspecting is always of typ `Vehicle`, as that's what we store in the `stock` list. That means that we won't be able to access any `Truck`'s `storageArea` variable. That's what the concept of *type casting* can be used for.
If you have a variable of a certain type, but you know for a fact that it's actually an instance of a *child class* of that type, then you can change that variable to be of the child class' type instead like so:
```java
Vehicle vehicle = stock.get(i);
if (vehicle instanceof Truck) {
Truck truck = (Truck) vehicle;
System.out.println("The " + i + "th truck can store " + truck.storageArea);
}
```
As you can see, to type cast a variable into a different type, all you have to do is write that type in parentheses `()` in front of the variable.[^1] Cool.
# Conclusion
So today, you learned one of the important aspects of object oriented programming, which is very useful in a heap of different scenarios, as you will be able to see throughout the next tutorials. Next time, we'll be covering overriding methods from superclasses, which will allow you to do even more cool stuff with inheritance.[^2]
I'm sorry that I've been taking such a long break from the tutorials, but after asking on Twitter if you people actually learn from them, a lot of you said that you do, and so that gave me the motivation to continue working on these.
So thanks a lot, and happy coding! <3
[^1]: As you might be able to see now, this concept is called type *casting* because it's like we're putting the vehicle into a mold (or a *cast*) to reshape it into a different type.
[^2]: I was originally planning on including that in this tutorial already, but I noticed that that might be a bit too much after all, so I'll be doing it next time instead.
For this tutorial, let's expand on the car dealership example from the end of the last tutorial: You have a car dealership, and you want to expand it to sell various other types of motor vehicles; let's say... motorbikes and trucks as well. Obviously, these different kinds of vehicles all have different properties: A motorbike requires the use of a helmet, and an important piece of information about a truck might be how big its storage area is.
Now, this is where object-oriented languages like Java shine.
# Extending Other Classes
The concept of extending other classes is probably best explained with an example, so to start out with, let's create a class that represents a car with a certain amount of wheels:
```java
public class Car {
public int amountOfWheels;
public Car(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
}
```
As you can see, this is just a simple class with a variable that can store how many wheels a vehicle has.
Now, let's imagine we want to create a class that represents a truck, and we would want any truck to store information on how many wheels it has, as well as how many cubic meters of storage it has available:
```java
public class Truck {
public int amountOfWheels;
public int storageArea;
public Truck(int amountOfWheels, int storageArea) {
this.amountOfWheels = amountOfWheels;
this.storageArea = storageArea;
}
}
```
As you can see, just creating the class like usual isn't really that pretty, because now both our `Car` class and our `Truck` class have separate `amountOfWheels` fields, despite the fact that they really refer to the same thing: The amount of wheels of a vehicle.
What we'd like to be able to do instead is to have the amount of wheels stored in a different location that both our `Truck` and our `Car` class can access. The first step of achieving this is creating another class that only stores information both of our vehicles should have:
```java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
}
```
Now, we can use the `extends` keyword that Java provides to cause both of our other classes to also have access to this information. Let's modify our `Car` class first and I'll explain afterwards what exactly our modification will do.
```java
public class Car extends Vehicle {
public Car(int amountOfWheels) {
// Note that the line below will be explained in a bit
super(amountOfWheels);
}
}
```
As you can see, our `Car` class now `extends Vehicle`. This means that the `Car` class is basically just an *upgraded* version of the `Vehicle` class: It contains all of the information that the `Vehicle` class provides (including fields and methods), but it can also provide its own additional information. From the perspective of `Car`, `Vehicle` is the *superclass*, and from the perspective of `Vehicle`, `Car` is a *child class*.
It should be noted that any class can only extend *one* other class, so `Car` couldn't extend both `Vehicle` and, let's say, `FueledDevice`. However, the other way around is possible: `Vehicle` can be extended by multiple classes, meaning that we can modify our `Truck` class to also be a child class like this:
```java
public class Truck extends Vehicle {
public int storageArea;
public Truck(int amountOfWheels, int storageArea) {
super(amountOfWheels);
this.storageArea = storageArea;
}
}
```
As you can see, the `Truck` class now uses the `amountOfWheels` field from its superclass, but it defines its own `storageArea` field that only trucks have.
## `super`
Now, you've probably been wondering what line 5 means: `super(amountOfWheels)`. As we defined earlier, our `Vehicle` class takes one argument in its constructor: The amount of wheels that it has. As we also previously discussed, a constructor *has to be called* when a new object is created. So, when creating a `Truck` object, the `Truck` constructor is called, but, in turn, it needs to also implicitly call the `Vehicle` constructor.
When placing a `super()` call at the start of the constructor of a class, the constructor of the class that is being extended will be called. If that class takes a set of arguments in its constructor, we have to pass it those arguments, similarly to if we were creating an object of that class.
That's what our `super(amountOfWheels)` is doing here: It's calling the constructor of `Vehicle` with the `amountOfWheels` argument.
It should be noted that we don't *necessarily* have to pass a variable into the super constructor. For instance, we know for a fact that a truck will always have four wheels, so we can modify our `Truck` constructor to always specify 4 as the wheel amount given to the superclass:
```java
public Truck(int storageArea) {
super(4);
this.storageArea = storageArea;
}
```
## The `Object` class
It should be noted at this point that *all classes* extend the `Object` class implicitly (meaning you never have to write `extends Object` anywhere and it will still hold true). This will be useful pretty soon.
## About Variable Types
Let's imagine we want to store the stock that we sell at our dealership, without specifying separate lists for the different kinds of vehicles we're selling. What we can do is have that variable be of type `Vehicle`, which will automatically allow instances of child classes to be stored in the variable (or list) like so:
```java
ArrayList<Vehicle> stock = new ArrayList<>();
stock.add(new Car(4));
stock.add(new Truck(10));
// or in the case of variables
Vehicle car = new Car(4);
Vehicle truck = new Truck(10);
```
# `instanceof` and Type Casting
Staying with our list of the stock we have, let's imagine we want to find all of the *trucks* that we have in stock, disregarding any other kinds of vehicles that we might have.
This is what the `instanceof` keyword can be used for: Using it, you can ask any object if it derives of a certain class; that is, if any object is an instance of the specified class.
Let's create a method to return a list of all of the cars we have in stock:
```java
import java.util.ArrayList;
public class Main {
private static ArrayList<Vehicle> stock = new ArrayList<>();
public static void main(String[] args) {
stock.add(new Car(4));
stock.add(new Truck(10));
getTrucks();
}
private static void getTrucks() {
for (int i = 0; i < stock.size(); i++) {
Vehicle vehicle = stock.get(i);
if (vehicle instanceof Truck) {
System.out.println("Vehicle " + i + " is a truck!");
}
}
}
}
```
As you can see, I've expanded the code from before to have a `getTrucks` method, which iterates through every vehicle we have in stock and if it finds a vehicle that is an instance of the `Truck` class, it will print out a message.
Another way to think about the `instanceof` keyword is as a weird, clunky synonym to the word "is": When asking `vehicle instanceof Truck`, we're really asking "Is this vehicle a truck?"
Now that we know which of the vehicles we have in stock are trucks, it might also be useful to know the storage capabilities of all of those trucks. However, as you can see, the `i`th vehicle we're currently inspecting is always of typ `Vehicle`, as that's what we store in the `stock` list. That means that we won't be able to access any `Truck`'s `storageArea` variable. That's what the concept of *type casting* can be used for.
If you have a variable of a certain type, but you know for a fact that it's actually an instance of a *child class* of that type, then you can change that variable to be of the child class' type instead like so:
```java
Vehicle vehicle = stock.get(i);
if (vehicle instanceof Truck) {
Truck truck = (Truck) vehicle;
System.out.println("The " + i + "th truck can store " + truck.storageArea);
}
```
As you can see, to type cast a variable into a different type, all you have to do is write that type in parentheses `()` in front of the variable.[^1] Cool.
# Conclusion
So today, you learned one of the important aspects of object oriented programming, which is very useful in a heap of different scenarios, as you will be able to see throughout the next tutorials. Next time, we'll be covering overriding methods from superclasses, which will allow you to do even more cool stuff with inheritance.[^2]
I'm sorry that I've been taking such a long break from the tutorials, but after asking on Twitter if you people actually learn from them, a lot of you said that you do, and so that gave me the motivation to continue working on these.
So thanks a lot, and happy coding! <3
***
[^1]: As you might be able to see now, this concept is called type *casting* because it's like we're putting the vehicle into a mold (or a *cast*) to reshape it into a different type.
[^2]: I was originally planning on including that in this tutorial already, but I noticed that that might be a bit too much after all, so I'll be doing it next time instead.

View file

@ -1,234 +1,227 @@
---
layout: blog
title: "☕ Java Tutorial, Part 7: Overriding Methods"
description: In this Java tutorial for beginners, we cover overriding methods, calling superclass methods and toString().
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1199339701640945664
archived: true
---
So it's been a hot minute since the last tutorial, and I apologize for that. However, it seems like there are some people that actually use these tutorials to properly learn Java, and so I didn't want to leave you all hanging.
Today's tutorial is going to cover method overrides, which are another awesome object orientation concept that will help you out greatly when programming.
Let's imagine that we want our different vehicle types (`Car` and `Truck`) from the last tutorial to be able to print out some information about themselves to the console. To do so, we could simply add a `printInformation()` method to each of the classes. However, that would end up being problematic if we wanted to print out information about *the entirety of our stock*, which, as you might recall, is stored in our `ArrayList<Vehicle> stock`, as we would have to create `instanceof` checks for both `Car` and `Truck` to be able to access their `printInformation()` methods.
An easy fix for that would be to create a basic `printInformation()` method in our base class (`Vehicle`) and then *override* that method in our subclasses, allowing us to change their behavior. Let's see what that would look like:
```java
// Vehicle.java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
public void printInformation() {
// Does nothing for now
}
}
// Car.java
public class Car extends Vehicle {
public Car(int amountOfWheels) {
super(amountOfWheels);
}
@Override
public void printInformation() {
System.out.println("This car has " + this.amountOfWheels + " wheels");
}
}
```
As you can see, I've added a simple `printInformation()` method to the `Vehicle` class (which does absolutely nothing for now). However, I have then *overridden* that method in the `Car` class. The way I've done that is by adding a method that has the exact same name, return type and accepted parameters as the base class's method. Technically, that would already be enough to override a method, however, to make it a little clearer to read, people usually like to add the `@Override` annotation[^1] above the method.
So what does this mean, exactly? Basically, when calling the `printInformation()` method on an instance of the `Vehicle` class, nothing will happen, because the `Vehicle`'s method is empty. However, when calling the method on an instance of the `Car` class, the print statement above will be executed, printing information about the car's wheel amount.
The important thing to understand is this: Which class's method is called isn't determined by the *variable type*, but by the type that the object itself has. Let's check out this example:
```java
// This is in our Main class from last time
private static void getTrucks() {
for (int i = 0; i < stock.size(); i++) {
Vehicle vehicle = stock.get(i);
vehicle.printInformation();
}
}
```
Now, each vehicle that isn't a car (so each truck in ou example) will not print out any information, but each of our cars will print out the information we specified above, despite the fact that we're not doing any `instanceof` checks or anything else. Cool.
# Calling `super` methods
Now, if we wanted to also add an override of the `printInformation()` method to our `Truck` class, we will come across a minor annoyance: To print out the truck's amount of wheels, we'd basically have to copy the print statement from our `Car` class, which is a bit ugly.
Well, that's where `super` calls come to the rescue! We've already briefly touched on super calls when talking about the super constructor for extending other classes, and super calls are very similar to that. Let's change our code up a bit, and then I'll explain what it all means.
```java
// Vehicle.java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
public void printInformation() {
System.out.println("Vehicle info:");
System.out.println("Wheel amount: " + this.amountOfWheels);
}
}
// Car.java
public class Car extends Vehicle {
public Car(int amountOfWheels) {
super(amountOfWheels);
}
@Override
public void printInformation() {
super.printInformation();
}
}
```
As you can see, I've modified the `Vehicle`'s method to print out the wheel amount by default, and I've changed the `Car`'s method to call `super.printInformation()`. What this call does is simply execute its parent class's `printInformation()` method, so a car will also have its wheel amount printed out just the same as any other vehicle.
It should be noted that simply calling the `super` method in an override and doing absolutely nothing else is *the default behavior*, meaning that, in the example above, we could leave out the `printInformation()` override in our `Car` class completely and still get the same effect.
Now let's add a `printInformation()` override to our `Truck` class as well, but this time, let's extend the behavior a bit:
```java
public class Truck extends Vehicle {
public int storageArea;
public Truck(int storageArea) {
super(4);
this.storageArea = storageArea;
}
@Override
public void printInformation() {
super.printInformation();
System.out.println("Storage area: " + this.storageArea);
}
}
```
Now, calling a truck's `printInformation()` method will *first* print out "Vehicle information:", then the wheel amount, and then the storage area. If we wanted this behavior to occur in a different order, we could simply swap the two lines as follows:
```java
@Override
public void printInformation() {
System.out.println("Storage area: " + this.storageArea);
super.printInformation();
}
```
Now, the storage area will be printed out *first*, followed by "Vehicle information:" and the wheel amount.[^2]
# `Object` methods
As I briefly mentioned in the last tutorial, *all* classes implicitly extend Java's `Object` class without you having to specify that information. This is finally going to be useful now, as this class provides some useful methods that we can override in our implementations.
## `toString()`
This method is probably the most versatile one of the bunch: It gets called whenever an object's information needs to be converted into a string in some circumstance. For example, if you simply write
```java
Car car = new Car(4);
System.out.println(car);
```
then the car's `toString()` method will be called inside of `println` in order to convert the car's information into a string.
However, by default, the `toString()` method simply prints out some not-so-useful information about the object's internal identifier. If we override this method, however, we can make it display some more useful information. Let's do so in our `Vehicle` and `Car` class as an example:
```java
// Vehicle.java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
@Override
public String toString() {
return "Vehicle with " + this.amountOfWheels + " wheels";
}
}
// Truck.java
public class Truck extends Vehicle {
public int storageArea;
public Truck(int storageArea) {
super(4);
this.storageArea = storageArea;
}
@Override
public String toString() {
String vehicleInfo = super.toString();
return vehicleInfo + " and " + this.storageArea + " storage area";
}
}
```
As you can see, `Truck` additionally calls the `toString()` super method and then appends some more information to the string created in `Vehicle`. Pretty nice.
## `equals()`
Remember how I told you that you should always use `equals()` to compare two strings instead of the double equals sign `==`?
Well, here's why: Java's `String` class overrides the `equals()` method from `Object` and changes its behavior so that two strings are considered equal if their contents are identical.
As an example, let's first override the `equals()` method in our `Vehicle` class with the default behavior that it would have if you didn't override it at all:
```java
@Override
public boolean equals(Object other) {
return this == other;
}
```
As you can see, the default behavior is simply the double equals sign `==`, which compares if two variable pointers point to the exact same object, as previously explained. That's not what we might want in our example, though.
Let's expand our `Vehicle` class to also have a unique identifier: The license plate's text. Let's say that we want to identify each vehicle by its plate, and so we make it the key factor in determining whether two vehicles are the same or not:
```java
public class Vehicle {
public int amountOfWheels;
public String licensePlate;
public Vehicle(int amountOfWheels, String licensePlate) {
this.amountOfWheels = amountOfWheels;
this.licensePlate = licensePlate;
}
@Override
public boolean equals(Object other) {
if (other instanceof Vehicle) {
Vehicle v = (Vehicle) other;
return v.licensePlate.equals(this.licensePlate);
}
return false;
}
}
```
As you can see, each `Vehicle` now accepts a license plate in the constructor, and the `equals()` method is overridden in a way that makes two vehicles be considered equal if their license plates match exactly.
Now, a great example of how this could be useful is with lists. The `ArrayList` class contains the `contains()` method, which determines if a certain element is already present in the list. Now, the cool thing is that this method uses the `equals()` method on each element to determine whether or not an element is present. We can use this behavior to check if a vehicle with a certain license plate is already in our stock pretty easily:
```java
import java.util.ArrayList;
public class Main {
private static ArrayList<Vehicle> stock = new ArrayList<>();
public static void main(String[] args) {
stock.add(new Car(4, "AC JS 1999"));
stock.add(new Truck(10, "AC NS 1998"));
Car carToCheck = new Car(4, "AC HI 1234");
if (stock.contains(carToCheck)) {
System.out.println("The queried car is already in our stock :)");
}
}
}
```
Obviously, in this specific example, the text won't be printed because there isn't a car with that license plate in the `stock` list.
# Conclusion
So yea, today you learned about the second key concept of object orientation in Java. To put this knowledge to the test, I'm going to give you an exercise all about extending classes and overriding methods that you can try to solve if you want.
> Let's imagine you're trying to create a program that allows you to draw manage different shapes, namely rectangles, right angle triangles and circles. Obviously, these shapes all have different properties, like the rectangle's and triangle's side lengths and the circle's radius. All of the shapes you currently have are stored in a list. For each of the shapes, you want to be able to calculate its circumference as well as its area. To test this program, you add a couple of shapes to your list and calculate the average of all of their areas and circumferences.
Note that you can find some methods and constants you might need in Java's default `Math` class, namely `Math.PI` and `Math.sqrt()`, the latter of which is used to calculate a square root. If you're stuck, you can [check my solution](https://gist.github.com/Ellpeck/8cf63c747313e070b7475d99e2bed5a1).
As always, happy coding!
[^1]: Annotations are another rather advanced topic that almost never comes up when using Java. They can be useful sometimes, but you'll hardly ever have a use for them.
[^2]: Which, in this case, would obviously be a bit of a nonsensical order to display this information in. But you get the idea.
So it's been a hot minute since the last tutorial, and I apologize for that. However, it seems like there are some people that actually use these tutorials to properly learn Java, and so I didn't want to leave you all hanging.
Today's tutorial is going to cover method overrides, which are another awesome object orientation concept that will help you out greatly when programming.
Let's imagine that we want our different vehicle types (`Car` and `Truck`) from the last tutorial to be able to print out some information about themselves to the console. To do so, we could simply add a `printInformation()` method to each of the classes. However, that would end up being problematic if we wanted to print out information about *the entirety of our stock*, which, as you might recall, is stored in our `ArrayList<Vehicle> stock`, as we would have to create `instanceof` checks for both `Car` and `Truck` to be able to access their `printInformation()` methods.
An easy fix for that would be to create a basic `printInformation()` method in our base class (`Vehicle`) and then *override* that method in our subclasses, allowing us to change their behavior. Let's see what that would look like:
```java
// Vehicle.java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
public void printInformation() {
// Does nothing for now
}
}
// Car.java
public class Car extends Vehicle {
public Car(int amountOfWheels) {
super(amountOfWheels);
}
@Override
public void printInformation() {
System.out.println("This car has " + this.amountOfWheels + " wheels");
}
}
```
As you can see, I've added a simple `printInformation()` method to the `Vehicle` class (which does absolutely nothing for now). However, I have then *overridden* that method in the `Car` class. The way I've done that is by adding a method that has the exact same name, return type and accepted parameters as the base class's method. Technically, that would already be enough to override a method, however, to make it a little clearer to read, people usually like to add the `@Override` annotation[^1] above the method.
So what does this mean, exactly? Basically, when calling the `printInformation()` method on an instance of the `Vehicle` class, nothing will happen, because the `Vehicle`'s method is empty. However, when calling the method on an instance of the `Car` class, the print statement above will be executed, printing information about the car's wheel amount.
The important thing to understand is this: Which class's method is called isn't determined by the *variable type*, but by the type that the object itself has. Let's check out this example:
```java
// This is in our Main class from last time
private static void getTrucks() {
for (int i = 0; i < stock.size(); i++) {
Vehicle vehicle = stock.get(i);
vehicle.printInformation();
}
}
```
Now, each vehicle that isn't a car (so each truck in ou example) will not print out any information, but each of our cars will print out the information we specified above, despite the fact that we're not doing any `instanceof` checks or anything else. Cool.
# Calling `super` methods
Now, if we wanted to also add an override of the `printInformation()` method to our `Truck` class, we will come across a minor annoyance: To print out the truck's amount of wheels, we'd basically have to copy the print statement from our `Car` class, which is a bit ugly.
Well, that's where `super` calls come to the rescue! We've already briefly touched on super calls when talking about the super constructor for extending other classes, and super calls are very similar to that. Let's change our code up a bit, and then I'll explain what it all means.
```java
// Vehicle.java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
public void printInformation() {
System.out.println("Vehicle info:");
System.out.println("Wheel amount: " + this.amountOfWheels);
}
}
// Car.java
public class Car extends Vehicle {
public Car(int amountOfWheels) {
super(amountOfWheels);
}
@Override
public void printInformation() {
super.printInformation();
}
}
```
As you can see, I've modified the `Vehicle`'s method to print out the wheel amount by default, and I've changed the `Car`'s method to call `super.printInformation()`. What this call does is simply execute its parent class's `printInformation()` method, so a car will also have its wheel amount printed out just the same as any other vehicle.
It should be noted that simply calling the `super` method in an override and doing absolutely nothing else is *the default behavior*, meaning that, in the example above, we could leave out the `printInformation()` override in our `Car` class completely and still get the same effect.
Now let's add a `printInformation()` override to our `Truck` class as well, but this time, let's extend the behavior a bit:
```java
public class Truck extends Vehicle {
public int storageArea;
public Truck(int storageArea) {
super(4);
this.storageArea = storageArea;
}
@Override
public void printInformation() {
super.printInformation();
System.out.println("Storage area: " + this.storageArea);
}
}
```
Now, calling a truck's `printInformation()` method will *first* print out "Vehicle information:", then the wheel amount, and then the storage area. If we wanted this behavior to occur in a different order, we could simply swap the two lines as follows:
```java
@Override
public void printInformation() {
System.out.println("Storage area: " + this.storageArea);
super.printInformation();
}
```
Now, the storage area will be printed out *first*, followed by "Vehicle information:" and the wheel amount.[^2]
# `Object` methods
As I briefly mentioned in the last tutorial, *all* classes implicitly extend Java's `Object` class without you having to specify that information. This is finally going to be useful now, as this class provides some useful methods that we can override in our implementations.
## `toString()`
This method is probably the most versatile one of the bunch: It gets called whenever an object's information needs to be converted into a string in some circumstance. For example, if you simply write
```java
Car car = new Car(4);
System.out.println(car);
```
then the car's `toString()` method will be called inside of `println` in order to convert the car's information into a string.
However, by default, the `toString()` method simply prints out some not-so-useful information about the object's internal identifier. If we override this method, however, we can make it display some more useful information. Let's do so in our `Vehicle` and `Car` class as an example:
```java
// Vehicle.java
public class Vehicle {
public int amountOfWheels;
public Vehicle(int amountOfWheels) {
this.amountOfWheels = amountOfWheels;
}
@Override
public String toString() {
return "Vehicle with " + this.amountOfWheels + " wheels";
}
}
// Truck.java
public class Truck extends Vehicle {
public int storageArea;
public Truck(int storageArea) {
super(4);
this.storageArea = storageArea;
}
@Override
public String toString() {
String vehicleInfo = super.toString();
return vehicleInfo + " and " + this.storageArea + " storage area";
}
}
```
As you can see, `Truck` additionally calls the `toString()` super method and then appends some more information to the string created in `Vehicle`. Pretty nice.
## `equals()`
Remember how I told you that you should always use `equals()` to compare two strings instead of the double equals sign `==`?
Well, here's why: Java's `String` class overrides the `equals()` method from `Object` and changes its behavior so that two strings are considered equal if their contents are identical.
As an example, let's first override the `equals()` method in our `Vehicle` class with the default behavior that it would have if you didn't override it at all:
```java
@Override
public boolean equals(Object other) {
return this == other;
}
```
As you can see, the default behavior is simply the double equals sign `==`, which compares if two variable pointers point to the exact same object, as previously explained. That's not what we might want in our example, though.
Let's expand our `Vehicle` class to also have a unique identifier: The license plate's text. Let's say that we want to identify each vehicle by its plate, and so we make it the key factor in determining whether two vehicles are the same or not:
```java
public class Vehicle {
public int amountOfWheels;
public String licensePlate;
public Vehicle(int amountOfWheels, String licensePlate) {
this.amountOfWheels = amountOfWheels;
this.licensePlate = licensePlate;
}
@Override
public boolean equals(Object other) {
if (other instanceof Vehicle) {
Vehicle v = (Vehicle) other;
return v.licensePlate.equals(this.licensePlate);
}
return false;
}
}
```
As you can see, each `Vehicle` now accepts a license plate in the constructor, and the `equals()` method is overridden in a way that makes two vehicles be considered equal if their license plates match exactly.
Now, a great example of how this could be useful is with lists. The `ArrayList` class contains the `contains()` method, which determines if a certain element is already present in the list. Now, the cool thing is that this method uses the `equals()` method on each element to determine whether or not an element is present. We can use this behavior to check if a vehicle with a certain license plate is already in our stock pretty easily:
```java
import java.util.ArrayList;
public class Main {
private static ArrayList<Vehicle> stock = new ArrayList<>();
public static void main(String[] args) {
stock.add(new Car(4, "AC JS 1999"));
stock.add(new Truck(10, "AC NS 1998"));
Car carToCheck = new Car(4, "AC HI 1234");
if (stock.contains(carToCheck)) {
System.out.println("The queried car is already in our stock :)");
}
}
}
```
Obviously, in this specific example, the text won't be printed because there isn't a car with that license plate in the `stock` list.
# Conclusion
So yea, today you learned about the second key concept of object orientation in Java. To put this knowledge to the test, I'm going to give you an exercise all about extending classes and overriding methods that you can try to solve if you want.
> Let's imagine you're trying to create a program that allows you to draw manage different shapes, namely rectangles, right angle triangles and circles. Obviously, these shapes all have different properties, like the rectangle's and triangle's side lengths and the circle's radius. All of the shapes you currently have are stored in a list. For each of the shapes, you want to be able to calculate its circumference as well as its area. To test this program, you add a couple of shapes to your list and calculate the average of all of their areas and circumferences.
Note that you can find some methods and constants you might need in Java's default `Math` class, namely `Math.PI` and `Math.sqrt()`, the latter of which is used to calculate a square root. If you're stuck, you can [check my solution](https://gist.github.com/Ellpeck/8cf63c747313e070b7475d99e2bed5a1).
As always, happy coding!
***
[^1]: Annotations are another rather advanced topic that almost never comes up when using Java. They can be useful sometimes, but you'll hardly ever have a use for them.
[^2]: Which, in this case, would obviously be a bit of a nonsensical order to display this information in. But you get the idea.

View file

@ -1,30 +1,21 @@
---
layout: blog
title: 😢 Lows
description: About depression and what it feels like when I don't know what to do with myself
tags: [Miscellaneous]
discuss: https://twitter.com/Ellpeck/status/1186028260838334471
archived: true
---
So some of you might be disappointed that this *isn't* the next Java tutorial. Well, today and the last couple of days, I haven't been feeling like working on those, because.. I've been depressed again. I never know how to describe it, or how to say it without seeming like it's just.. something I say when I'm too lazy to work on things.
This post will probably be a little all over the place, because that's also the mood that my head is currently in, but I'll try to explain to you what it feels like to me to have a low low follow a high high, mood-wise.
# Highs
When I say that I'm depressed, it means something different than when I say that I'm suffering from depression. Because, for me, depression isn't a constant state. Before depression hits, I have long periods of highs, long periods of what non-depressed people would probably just call.. normal moods.
I get up in the mornings, I enjoy breakfast, I go to University, I do my daily chores, I sit down at my computer, I code, I play some games, and then I go to bed. Normal days, really.
And if you don't suffer from depression, then you might think "Well, what's so special about that?", and the annoying thing is: That's *exactly* what I also think when I'm experiencing a high. I take it for granted, and I never even stop to think about how great it is that I'm currently feeling normal about my life.
# Lows
And then a low hits. And the longer the high has been going on for, the longer I was happy for, the harder it hits. Right now, I've been feeling low for two days, after a high that lasted almost *a month*. When I'm going through a low phase like now, my day works a lot differently.
I wake up at noon. I stay in bed. I check my phone. Hours pass. I think to myself "What am I doing?" I check my phone. After a while, I force myself to get up; I might even take a shower. I find some cookies in the back of my fridge; those'll be good enough for breakfast, I think to myself. I sit down at my computer. I watch some videos. "What am I doing?" Maybe some more videos. "What am I doing? I don't know what to do." I start Minecraft. "This sounds like fun!" I play for five minutes. I close the game again. "What am I doing?" I watch some videos. I start The Sims 4. I play for half an hour, and then I close the game again. "What am I doing?" I watch some more YouTube videos. I listen to some music. Should I do my coursework now? Not in the mood. I open IntelliJ. Let's work on some code. I glance at a couple of files, and then I close it again. "What am I doing?"
Take that whole description, and slow it down by about 200%. That's what a low day feels like to me. I feel like every single hour; every single *minute* even, is filled with doubt and emptiness and just... nothing.
This is so hard to explain for me because I also don't.. see myself in these low phases. I don't look at myself. I don't observe what I'm doing. It's like I'm not even there; it's like I'm not inside my own body, doing these activities with it. It's like it's just doing everything on some sort of weird auto-pilot mode.
So until that passes, I probably won't be working on any tutorials, or anything, really. Let's hope it passes fairly soon, because I'm honestly sick of forcing myself to eat cookies for breakfast. It might sound great, but eating something when you have absolutely no desire to eat things is... quite unpleasant, really.
So some of you might be disappointed that this *isn't* the next Java tutorial. Well, today and the last couple of days, I haven't been feeling like working on those, because.. I've been depressed again. I never know how to describe it, or how to say it without seeming like it's just.. something I say when I'm too lazy to work on things.
This post will probably be a little all over the place, because that's also the mood that my head is currently in, but I'll try to explain to you what it feels like to me to have a low low follow a high high, mood-wise.
# Highs
When I say that I'm depressed, it means something different than when I say that I'm suffering from depression. Because, for me, depression isn't a constant state. Before depression hits, I have long periods of highs, long periods of what non-depressed people would probably just call.. normal moods.
I get up in the mornings, I enjoy breakfast, I go to University, I do my daily chores, I sit down at my computer, I code, I play some games, and then I go to bed. Normal days, really.
And if you don't suffer from depression, then you might think "Well, what's so special about that?", and the annoying thing is: That's *exactly* what I also think when I'm experiencing a high. I take it for granted, and I never even stop to think about how great it is that I'm currently feeling normal about my life.
# Lows
And then a low hits. And the longer the high has been going on for, the longer I was happy for, the harder it hits. Right now, I've been feeling low for two days, after a high that lasted almost *a month*. When I'm going through a low phase like now, my day works a lot differently.
I wake up at noon. I stay in bed. I check my phone. Hours pass. I think to myself "What am I doing?" I check my phone. After a while, I force myself to get up; I might even take a shower. I find some cookies in the back of my fridge; those'll be good enough for breakfast, I think to myself. I sit down at my computer. I watch some videos. "What am I doing?" Maybe some more videos. "What am I doing? I don't know what to do." I start Minecraft. "This sounds like fun!" I play for five minutes. I close the game again. "What am I doing?" I watch some videos. I start The Sims 4. I play for half an hour, and then I close the game again. "What am I doing?" I watch some more YouTube videos. I listen to some music. Should I do my coursework now? Not in the mood. I open IntelliJ. Let's work on some code. I glance at a couple of files, and then I close it again. "What am I doing?"
Take that whole description, and slow it down by about 200%. That's what a low day feels like to me. I feel like every single hour; every single *minute* even, is filled with doubt and emptiness and just... nothing.
This is so hard to explain for me because I also don't.. see myself in these low phases. I don't look at myself. I don't observe what I'm doing. It's like I'm not even there; it's like I'm not inside my own body, doing these activities with it. It's like it's just doing everything on some sort of weird auto-pilot mode.
So until that passes, I probably won't be working on any tutorials, or anything, really. Let's hope it passes fairly soon, because I'm honestly sick of forcing myself to eat cookies for breakfast. It might sound great, but eating something when you have absolutely no desire to eat things is... quite unpleasant, really.

129
blog/posts.json Normal file
View file

@ -0,0 +1,129 @@
[{
"name": "Blogs are Cool, I Think",
"summary": "The first post and how I created it",
"id": "blogs_are_cool",
"date": "2/17/2019",
"discuss": "https://twitter.com/Ellpeck/status/1096937184601538566",
"archived": true
},
{
"name": "Why You Should Mod Minecraft",
"summary": "About what makes Minecraft modding great and why you should probably try it if you enjoy programming",
"id": "why_you_should_mod_minecraft",
"date": "2/17/2019",
"discuss": "https://twitter.com/Ellpeck/status/1097177774337462272"
},
{
"name": "Kindling the Reading Flame",
"summary": "About the Kindle, Harry Potter and what I like and dislike about them",
"id": "reading",
"date": "3/22/2019",
"discuss": "https://twitter.com/Ellpeck/status/1109102077911973888"
},
{
"name": "Small Projects",
"summary": "Why creating a small, unplanned project is sometimes good for you",
"id": "small_projects",
"date": "5/1/2019",
"discuss": "https://twitter.com/Ellpeck/status/1123651624201871360"
},
{
"name": "About Cross-Platform and Motivation",
"summary": "How moving from Java to C# taught me how horrible it is to create a cross-platform application with little to no knowledge or documentation",
"id": "cross_platform_trainwreck",
"date": "7/6/2019",
"discuss": "https://twitter.com/Ellpeck/status/1147502654236573697",
"archived": true
},
{
"name": "Big Projects",
"summary": "How a once small project I even created a post about turned into the first game I'm selling: Foe Frenzy",
"id": "big_projects",
"date": "9/15/2019",
"discuss": "https://twitter.com/Ellpeck/status/1173247686654517249"
},
{
"name": "How to make a Rock Bottom mod",
"summary": "My adventures back into a game I stopped working on about two years ago and how I start on a mod for it",
"id": "rock_bottom_mod",
"date": "10/3/2019",
"discuss": "https://twitter.com/Ellpeck/status/1180092634410487808"
},
{
"name": "Java Tutorial, Part 1: Hello World",
"summary": "The first part of my post series for programming beginners where I explain how to write code in Java.",
"id": "java_1",
"date": "10/10/2019",
"discuss": "https://twitter.com/Ellpeck/status/1182080078827737088"
},
{
"name": "Java Tutorial, Part 2: Intro to Conditions and Loops",
"summary": "The second part of my post series for programming beginners. This one is all about conditions and loops.",
"id": "java_2",
"date": "10/10/2019",
"discuss": "https://twitter.com/Ellpeck/status/1182354544707198976"
},
{
"name": "Java Tutorial, Part 3: (Static) Methods",
"summary": "In this Java tutorial for beginners, we cover what (static) methods, parameters and return types are.",
"id": "java_3",
"date": "10/11/2019",
"discuss": "https://twitter.com/Ellpeck/status/1182775985885847558"
},
{
"name": "Java Tutorial, Part 4: Classes and Objects",
"summary": "In this Java tutorial for beginners, we cover the basics of creating classes with a constructor, some fields and some methods and creating objects of them.",
"id": "java_4",
"date": "10/14/2019",
"discuss": "https://twitter.com/Ellpeck/status/1183857460660101133"
},
{
"name": "Java Tutorial, Part 5: Things I Left Out So Far",
"summary": "In this Java tutorial for beginners, we cover some shorthands, some more data types, the difference between pass-by-reference and pass-by-value, null, as well as arrays and lists.",
"id": "java_5",
"date": "10/17/2019",
"discuss": "https://twitter.com/Ellpeck/status/1184894859133509632"
},
{
"name": "Lows",
"summary": "About depression and what it feels like when I don't know what to do with myself",
"id": "lows",
"date": "10/20/2019",
"discuss": "https://twitter.com/Ellpeck/status/1186028260838334471",
"archived": true
},
{
"name": "Java Tutorial, Part 6: Inheritance",
"summary": "In this Java tutorial for beginners, we cover classes extending other classes and the instanceof keyword.",
"id": "java_6",
"date": "10/31/2019",
"discuss": "https://twitter.com/Ellpeck/status/1189904487722487809"
},
{
"name": "Java Tutorial, Part 7: Overriding Methods",
"summary": "In this Java tutorial for beginners, we cover overriding methods, calling superclass methods and toString().",
"id": "java_7",
"date": "11/26/2019",
"discuss": "https://twitter.com/Ellpeck/status/1199339701640945664"
},
{
"name": "But Do You Really Care?",
"summary": "On taking a break from social media",
"id": "but_do_you_really_care",
"date": "5/6/2020"
},
{
"name": "Oh God, Please Don't Port Actually Additions",
"summary": "As Actually Additions celebrates its fifth birthday, I break down what I like and dislike about it.",
"id": "actually_additions",
"date": "5/10/2020",
"discuss": "https://twitter.com/Ellpeck/status/1259600490377216002"
},
{
"name": "The Future of Actually Additions",
"summary": "Not wanting to accept the fate of Actually Additions, someone has come to its rescue. 1.16, here we come?",
"id": "future_actually_additions",
"date": "11/23/2020",
"discuss": "https://twitter.com/Ellpeck/status/1330938597785169925"
}
]

19
blog/reading.md Normal file
View file

@ -0,0 +1,19 @@
Recently, my boyfriend gave me his old Amazon Kindle (because he got one of the newer models). This inspired me, after about four or five years of not really picking up a book outside of school, to start reading for fun again. The first book that I "picked up" was Harry Potter and the.. stone.. of the wise ("Harry Potter and the Philosopher's Stone" apparently).
# The book
I've never written a book review before, but since Harry Potter is kind of a really popular genre and this is the first time I read one of the books, I wanted to give my opinion on it.
Now usually, I don't really enjoy the fantasy genre. Something about it is just a bit uninteresting to me, and I don't necessarily enjoy reading something that's unrelated to (my) reality. I usually need entertainment to be somewhat relatable to me and my life, and fantasy and science fiction don't always do that for me; I don't really enjoy things like Star Wars, but I do enjoy, for example, the Netflix show Black Mirror, because it touches topics that I do come in contact with in my own life.
But because the first Harry Potter book introduced the entire thing in such a great way, making the reader follow along with Harry's journey of _also_ figuring out all of the magic stuff that is new to both him and the reader, it felt like a lot better of an introduction to me.
Reading the first three quarters or so of the book, I honestly had a blast. The story was being told so well and all of the details and emotions were depicted so extensively, the places were described with a depth that made me be able to envision them perfectly in my head.
But then, to my (negative) surprise, suddenly, towards the last couple of chapters of the book, the tone shifted. All of a sudden, the story was being told in such a rushed, quick way; especially what should've been the climax of the book. To me, it felt like the author was getting scared of missing her deadline and stopped writing in the way that I had started to love so much.
I would definitely say that I enjoyed the book (which is saying quite a lot, because fantasy is definitely not my kind of genre), and I plan on also reading the other entries in the series, but to me, it felt like the later in the book I was, the more the quality of what I was reading was declining, which was such a bummer to me because J.K. Rowling seems to be a _really_ good author.
_Also, regarding the stuff that's happening with whatever J.K. is saying right now: If she or anyone else thinks it's okay to retroactively pretend like there's diversity where there simply isn't any: In my opinion, it's not okay. Just accept that your writing in that regard just isn't up to the standard of what it should be in the 21st century. Characters shouldn't be gay, black or another minority for the sake of "showing diversity" or "making a statement", but because diversity like that just happens to exist in the real world._
# The Kindle
There's honestly not much to say about the Kindle, because everything that I could say can be easily summed up into this: _I absolutely love it_. It's one of the older generation Paperwhites (maybe even the first one, I'm not sure), and the e-ink is amazing, the menus are easy to navigate, being able to change the font is great. It's just all round an awesome experience, especially because, with Amazon Prime, which pretty much everyone seems to have, you have Prime Reading, which allows you to read quite a big amount of books for free (without even having to get Kindle Unlimited).
Seriously, if you enjoy reading, but don't enjoy carrying heavy books around with you while you're traveling, I really think you should get a Kindle.

View file

Before

Width:  |  Height:  |  Size: 632 KiB

After

Width:  |  Height:  |  Size: 632 KiB

View file

Before

Width:  |  Height:  |  Size: 380 KiB

After

Width:  |  Height:  |  Size: 380 KiB

View file

Before

Width:  |  Height:  |  Size: 630 KiB

After

Width:  |  Height:  |  Size: 630 KiB

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

View file

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5 KiB

View file

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 145 KiB

View file

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

View file

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 156 KiB

View file

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View file

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

View file

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View file

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 158 KiB

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View file

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View file

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View file

Before

Width:  |  Height:  |  Size: 4.3 MiB

After

Width:  |  Height:  |  Size: 4.3 MiB

View file

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View file

@ -1,12 +1,4 @@
---
layout: blog
title: ⬇️ How to make a Rock Bottom mod
description: My adventures back into a game I stopped working on about two years ago and how I start on a mod for it
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1180092634410487808
---
So it's been a hot minute since I stopped working on my first big game project, [Rock Bottom](https://rockbottomgame.com). Since then, I've changed a lot, but the game hasn't changed that much: For a long time, the project was vacant, until I decided to make it open source. From that point on, a couple of my friends started working on it, adding some new features and fixing some bugs, until it seemingly fell back into vacancy over the last couple of weeks.
So it's been a hot minute since I stopped working on my first big game project, [Rock Bottom](https://rockbottomgame.com). Since then, I've changed a lot, but the game hasn't changed that much: For a long time, the project was vacant, until I decided to make it open source. From that point on, a couple of my friends started working on it, adding some new features and fixing some bugs, until it seemingly fell back into vacancy over the last couple of weeks.
So let's port my recent Minecraft mod, [Nature's Aura](https://www.curseforge.com/minecraft/mc-mods/natures-aura), to Rock Bottom!
@ -21,7 +13,7 @@ Okay, finding [a build of the game](https://github.com/RockBottomGame/RockBottom
Hm. Okay.
So something's already broken.
![](rbm_1.png)
![](blog/res/rock_bottom_mod/1.png =100%x*)
It looks like here
```
@ -35,7 +27,7 @@ That didn't seem to fix it either. Huh. Taking a look at [the maven](https://mav
Okay, it's now the next day and it looks like the maven has been fixed, which is nice. So all the compile issues are finally resolved, I put the build into the `/gamedata` folder like explained in the tutorial, I renamed the examplemod to `NaturesAura`, and I can now finally try running the game!
![](rbm_2.png)
![](blog/res/rock_bottom_mod/2.png =100%x*)
Ah! That worked quite well in the end.
# Actually making something
@ -84,7 +76,7 @@ public void preInit(IGameInstance game, IApiHandler apiHandler, IEventHandler ev
So far, so good. Let's figure out how to add a texture to the thing.
I somewhat remember that I made a horrible json-based asset system (instead of just loading all of the assets in the mod's jar automatically), so I'm going to try to add my tile to the `assets.json` file the example mod provided me with and also add a texture into the actual file system.
![](rbm_3.png)
![](blog/res/rock_bottom_mod/3.png =100%x*)
This is the folder structure I decided on. I also created a quick golden version of the game's leaves texture by going to the [asset repository](https://github.com/RockBottomGame/Assets), stealing the leaves texture and recoloring it to be golden-ish.
Also, I put this in the assets file, but I have no idea if that's actually the right path. We'll find out.
@ -109,7 +101,7 @@ public static final Tile GOLDEN_LEAVES = new TileGoldenLeaves(NaturesAura.create
```
*Crisis averted*.
![](rbm_4.png)
![](blog/res/rock_bottom_mod/4.png =100%x*)
Yaaay, it... worked? *Somewhat?*
You're probably yelling at your screen by now, but yes, I finally noticed it as well: My assets path says `examplemod` instead of `naturesaura`. Easy fix, though.
@ -127,7 +119,7 @@ While I'm at it though, I can also add a localization entry for the golden leave
Let's try again.
![](rbm_5.png)
![](blog/res/rock_bottom_mod/5.png =100%x*)
Ta-da! Success, at last.
After some investigation, I realized that Rock Bottom's normal leaves can be walked through, so let's see how to make that happen. Typing `@Override` while in the `TileGoldenLeaves` class causes my IDE to list all of the methods I can override. Among them are two that interest me:
@ -144,13 +136,13 @@ public BoundBox getBoundBox(IWorld world, TileState state, int x, int y, TileLay
```
It seems like this is what I have to do to make the tile walk-through...able. Let's try it out.
![](rbm_6.png)
![](blog/res/rock_bottom_mod/6.png =100%x*)
Yay, that seems to have worked. Great.
# Making an item
Now that I have somewhat of a grip of this whole Rock Bottom stuff again, I'm quickly going to make an item. I won't bore you with the details as it's pretty similar to making a tile, but the gist of it is this: I made an `ItemGoldPowder` class that extends `ItemBasic`, and I initialized and registered an instance of that in my newly created `Items` class, of which I created an instance in my mod class's `preInit` method so that it gets initialized at the right time. Also, I did all of the annoying asset mumbo jumbo.
![](rbm_7.png)
![](blog/res/rock_bottom_mod/7.png =100%x*)
*Oh, C#, you've ruined me.*
# Making stuff happen
@ -172,7 +164,7 @@ While writing this code, I quickly remembered that Rock Bottom has a tile state
Let's try it out! ...yea, no. The Java version that gradle uses to compile a Rock Bottom mod isn't new enough yet: You can't use the `var` keyword. Oh, my poor, poor C# soul. So let's swap that `var` out for a `TileState`.
![](rbm_8.gif)
![](blog/res/rock_bottom_mod/8.gif =100%x*)
Yay, it works! *Except that, in the gif, the mouse position is weirdly offset for some reason. It's correct in person, I promise!*
# Conclusion
@ -180,6 +172,6 @@ So yea, that was the start of my adventure back into Java, back into my old game
I think it's important to remember that, as a developer (especially an indie developer), you don't have to code everything perfectly or work stuff out correctly the first try. I mean, heck, this is my own game, and I completely forgot how to make a mod for it. But it was fun to figure it out again, and to get back into something I made two years ago.
Oh, also, if you really want, [here's a build of the mod](NaturesAuraRockBottom-0.1.jar) that you can try out yourself, as the game is now actually open source and available to everyone! I created this jar with the command `gradlew build`, and all you have to do to run it is [download the game](https://github.com/RockBottomGame/RockBottom/releases), run it once, and then stick the mod jar into its `mods` folder. It really doesn't do that much right now, though, so I don't know why you'd bother.
Oh, also, if you really want, [here's a build of the mod](https://ellpeck.de/blog/res/rock_bottom_mod/NaturesAuraRockBottom-0.1.jar) that you can try out yourself, as the game is now actually open source and available to everyone! I created this jar with the command `gradlew build`, and all you have to do to run it is [download the game](https://github.com/RockBottomGame/RockBottom/releases), run it once, and then stick the mod jar into its `mods` folder. It really doesn't do that much right now, though, so I don't know why you'd bother.
As always, thanks for reading!
As always, thanks for reading!

View file

@ -1,30 +1,22 @@
---
layout: blog
title: 🗡️ Small Projects
description: Why creating a small, unplanned project is sometimes good for you
tags: [Programming]
discuss: https://twitter.com/Ellpeck/status/1123651624201871360
---
As it turns out, I struggle a lot with maintaining interest and motivation for working on bigger projects like my [other game](https://rockbottom.ellpeck.de/) or my Minecraft mods. However, an easy fix for that would be to just start less big projects and focus more on smaller ones, like **Foe Frenzy**. Foe Frenzy is a game I've been working on for about a month now.
![](contrib.png)
![](blog/res/small_projects/contrib.png =100%x*)
The basic gameplay is finished, as is enough content for my friends to have been testing it for the last week or so and saying that they enjoy it quite a bit, both visually and gameplay-wise. I thought I'd make a blog post about my process of working on it, showing what it's about and what I did this time around to make sure that I don't get burnt out with it.
![](overview.png)
![](blog/res/small_projects/overview.png =100%x*)
_Foe Frenzy is a fast-paced fighting game where you battle up to three of your friends with random, short-lasting items in an attempt to be the last survivor._
# The Artstyle
Not every developer has this problem, but I know a lot do: The inability to make good-looking art. Well, I have this same problem too.
A lot of times, I'll find myself getting stuck on making art for my game projects and getting burnt out through trying too hard to force myself to make good-looking art.
A lot of times, I'll find myself getting stuck on making art for my game projects and getting burnt out through trying too hard to force myself to make good-looking art.
So, this time around, what I did instead of doing that all over again, I just decided to go with _simple art_ instead. For example, as you can see, all the tiles in the world only have 8x8 pixels, and all of them follow a really simple pattern: The top and left side are slightly darker, giving the world a really tiled feel and making the levels seem like they were made with actual building bricks. As well as that, the character movement animations are _really_ simple.
![](wobble.gif)
![](blog/res/small_projects/wobble.gif =100%x*)
![](water_wobble.gif)
![](blog/res/small_projects/water_wobble.gif =100%x*)
Both of those aren't even real animations per se, because what they really are is just the player's static texture rotating around a sin wave that is centered on the player's feet. It looks cute, though.
@ -35,7 +27,7 @@ What I did for this game instead is just kind of... let the code take me where i
For example, the map parsing system is really simple: It consists of an XML file, which stores all the important information about a map like its name and the positions that players spawn in, and a png file.
![](map.png)
![](blog/res/small_projects/map.png =100%x*)
Each pixel in the file determines which tile will be put at its location. In this instance, black is deep water, blue is shallow water, yellow is sand and green is grass. And the cool thing is that this is really easy to deal with (reading pixels is a simple thing that's present in almost every game framework), because changing something on the map just takes an image editing program, no custom map editor needed.
@ -53,18 +45,18 @@ Because I had this clear plan in mind before starting the game, and because it's
# So yea...
that's what I've been doing for the last month, and that's why [my other projects](https://minecraft.curseforge.com/projects/natures-aura) have been suffering a little bit. But it's been really fun, and I might even have something to actually release as a real, finished game some time in the near-ish future. It's also been making me feel accomplished, because I've already been able to have my friends test the game out, and they say it's been fun which means that I already accomplished a big part of what I wanted to accomplish with this game.
Keep updated about the game on [my Discord](https://link.ellpeck.de/discordweb) as well as [my Twitter](https://twitter.com/Ellpeck), and I hope you enjoyed reading about the game and my development process so far.
Keep updated about the game on [my Discord](https://ellpeck.de/discord) as well as [my Twitter](https://twitter.com/Ellpeck), and I hope you enjoyed reading about the game and my development process so far.
To finish off, here are some more pictures of the game for your enjoyment.
![](cave.png)
![](blog/res/small_projects/cave.png =100%x*)
A cave being lit up by torches and lava. This is where a rubber hammer can spawn that you can use to knock the other players into the lava.
![](map_select.png)
![](blog/res/small_projects/map_select.png =100%x*)
The map selection screen, where each player can choose the map that they'd like to play most.
![](flame_thrower.gif)
![](blog/res/small_projects/flame_thrower.gif =100%x*)
The favorite item I've added so far: The flamethrower.
![](win.gif)
Killing the last person alive and winning
![](blog/res/small_projects/win.gif =100%x*)
Killing the last person alive and winning

View file

@ -1,52 +1,44 @@
---
layout: blog
title: ⚙️ Why You Should Mod Minecraft
description: About what makes Minecraft modding great and why you should probably try it if you enjoy programming
tags: [Minecraft, Programming]
discuss: https://twitter.com/Ellpeck/status/1097177774337462272
---
So... for the last couple of years, the main thing I've been doing in my free time is game development and modding Minecraft, but the latter far more often and more consistently. I've learned a lot while doing this, but not only when it comes to programming and game development, but also when it comes to a lot of other things.
So in this post, I want to introduce you to what's so great about Minecraft modding and, honestly, why you should try doing it as well.
# Programming Experience
Now, first and foremost, and I think this is definitely one of the most obvious point: It improves your programming experience by quite a bit. Both in the "understanding the language" department, but also, it helps you to do a couple of things that you'll need if you want to deal with anything that other people have made:
- Understanding someone else's codebase and learning to navigate code that someone else has made, both documented and undocumented
- The beauty of learning how annoying it is to see code that's made in a way that makes it *so unbelievably hard* to expand it, to build on it, and learning that, when making your own code, you most definitely shouldn't do it like that.
- Learning to make something fit well with something other people have made. I'm sure this is also a useful skill to have in a job that includes team efforts: The same way a paper should have a unified style, it's also not very nice to have high resolution textures in a mod for a game that has a very primitive artstyle.
# Easily Creating Things
When creating your own game, I've personally found that it's pretty hard to get it off the ground at the start, because all you can see for the first couple of days are missing textures, placeholders, a missing main menu, janky controls and so on. When you're making a mod for any game, but especially for Minecraft, you can create a single, small feature and then instantly start using it together with all of the other features that the game already provides for you.
It's so much easier to get excited and satisfied about something you've made when it doesn't take ages for it to actually look good in its intended environment - making a mod for a game is perfect for that.
# Meeting Amazing People
This is something that definitely comes a bit later down the line, at least it did for me, but it's also something that's been really, really important to me. Through modding Minecraft, and especially by making [Actually Additions](https://minecraft.curseforge.com/projects/actually-additions), I've met so many amazing people and made a huge amount of new friends.
And not only that, I've also met a lot of people that I really look(ed) up to and respect(ed), and then noticed how down to earth and nice they actually are (because after all, they're also just people): Direwolf20, Vazkii and so many other people became my friends because it's easy to get along well with someone that shares the same interests as you, especially when it's an interest as passionate as the Minecraft Modding community.
# Being Validated by Thousands
This is one of the most important bits for me, honestly. I have pretty low self esteem (as a lot of introverted people on the internet do, I imagine), and it's hard for me to be proud of something that I've created.
But through the mods I've made, especially Actually Additions and recently also [Nature's Aura](https://minecraft.curseforge.com/projects/natures-aura), I've been shown by so many people, both friends and complete strangers, that the stuff I make seems to be worth quite a lot to quite a lot of people, and that they're passionate about my work, some maybe even more passionate than I am about it myself.
It feels so good to make something and to see people downloading it, people playing with it, people making videos about it and saying how nice they think it is. Trying to be validated isn't "seeking attention", it's good for you, because it makes you feel like you're worth a lot. Because you are.
# Learning to Deal With Criticism
Also a big thing for me for sure. Because of my previously mentioned low self esteem, it's been pretty hard for me to accept and deal with people saying that they dislike something that I've created.
But in the Minecraft modding community, oh boy, it's a *huge* thing. The first thing you'll get to hear about any new mod you publish is how it's "*totally a ripoff of Thaumcraft and Botania*" and "*this is just Extra Utilities, but worse*", and so on, and so forth. And.. at first, it can be quite demotivating, to say the least, because it sucks that people compare your thing to someone else's thing, despite the fact that you put at least an equal amount of work in it, and despite the fact that it deserves the same amount of respect as that other thing.
But that's just the way people are, and sooner or later, you have to learn to accept that. And you will. Positive people will still be there, and the stuff you've made will still be loved by a lot of them.
And it's not all bad either, in fact, the opposite is true: There are a *lot* of people that give you constructive criticism: "*I enjoyed this, but I think x, y and z could be changed and it would be even better.*" If you're the kind of person that says this, then you're a winner, because this helps not only the thing you're commenting on, but also the person that's made the thing: Now they know what you like, what you don't like, but most importantly: *How to fix it for you*. And the best thing is: You as the creator don't even have to listen to them if you disagree! They'll probably understand, because they're nice enough not to be dicks like the people I talked about before.
So keep on going, no matter what people say, because you're probably awesome.
# Basically No Obligations
When making a game, or a "real" product in general, a lot of the time, you're bound to certain obligations: Either it's legal ones, or maybe you've done a Kickstarter, maybe the thing you make actually costs you quite a bit of money because you have a whole development team, stuff like that.
Well with Minecraft modding, the only thing you have to deal with is people asking you when you'll *finally* update to the next version. But if you're sick of that, or sick of anything else, you can just stop, take a break, and do something else entirely for a while.
People might judge, but the important thing is: They don't have a right to judge, because you can do whatever you want. It's a hobby thing, so it's your thing.
# So yea,
this is all of the stuff I could think of that inspires me to keep going with Minecraft Modding. It's been a really awesome journey so far and I'm excited to keep going, to create more things and to see more things that other people created.
Thanks for reading! <3
So... for the last couple of years, the main thing I've been doing in my free time is game development and modding Minecraft, but the latter far more often and more consistently. I've learned a lot while doing this, but not only when it comes to programming and game development, but also when it comes to a lot of other things.
So in this post, I want to introduce you to what's so great about Minecraft modding and, honestly, why you should try doing it as well.
# Programming Experience
Now, first and foremost, and I think this is definitely one of the most obvious point: It improves your programming experience by quite a bit. Both in the "understanding the language" department, but also, it helps you to do a couple of things that you'll need if you want to deal with anything that other people have made:
- Understanding someone else's codebase and learning to navigate code that someone else has made, both documented and undocumented
- The beauty of learning how annoying it is to see code that's made in a way that makes it *so unbelievably hard* to expand it, to build on it, and learning that, when making your own code, you most definitely shouldn't do it like that.
- Learning to make something fit well with something other people have made. I'm sure this is also a useful skill to have in a job that includes team efforts: The same way a paper should have a unified style, it's also not very nice to have high resolution textures in a mod for a game that has a very primitive artstyle.
# Easily Creating Things
When creating your own game, I've personally found that it's pretty hard to get it off the ground at the start, because all you can see for the first couple of days are missing textures, placeholders, a missing main menu, janky controls and so on. When you're making a mod for any game, but especially for Minecraft, you can create a single, small feature and then instantly start using it together with all of the other features that the game already provides for you.
It's so much easier to get excited and satisfied about something you've made when it doesn't take ages for it to actually look good in its intended environment - making a mod for a game is perfect for that.
# Meeting Amazing People
This is something that definitely comes a bit later down the line, at least it did for me, but it's also something that's been really, really important to me. Through modding Minecraft, and especially by making [Actually Additions](https://minecraft.curseforge.com/projects/actually-additions), I've met so many amazing people and made a huge amount of new friends.
And not only that, I've also met a lot of people that I really look(ed) up to and respect(ed), and then noticed how down to earth and nice they actually are (because after all, they're also just people): Direwolf20, Vazkii and so many other people became my friends because it's easy to get along well with someone that shares the same interests as you, especially when it's an interest as passionate as the Minecraft Modding community.
# Being Validated by Thousands
This is one of the most important bits for me, honestly. I have pretty low self esteem (as a lot of introverted people on the internet do, I imagine), and it's hard for me to be proud of something that I've created.
But through the mods I've made, especially Actually Additions and recently also [Nature's Aura](https://minecraft.curseforge.com/projects/natures-aura), I've been shown by so many people, both friends and complete strangers, that the stuff I make seems to be worth quite a lot to quite a lot of people, and that they're passionate about my work, some maybe even more passionate than I am about it myself.
It feels so good to make something and to see people downloading it, people playing with it, people making videos about it and saying how nice they think it is. Trying to be validated isn't "seeking attention", it's good for you, because it makes you feel like you're worth a lot. Because you are.
# Learning to Deal With Criticism
Also a big thing for me for sure. Because of my previously mentioned low self esteem, it's been pretty hard for me to accept and deal with people saying that they dislike something that I've created.
But in the Minecraft modding community, oh boy, it's a *huge* thing. The first thing you'll get to hear about any new mod you publish is how it's "*totally a ripoff of Thaumcraft and Botania*" and "*this is just Extra Utilities, but worse*", and so on, and so forth. And.. at first, it can be quite demotivating, to say the least, because it sucks that people compare your thing to someone else's thing, despite the fact that you put at least an equal amount of work in it, and despite the fact that it deserves the same amount of respect as that other thing.
But that's just the way people are, and sooner or later, you have to learn to accept that. And you will. Positive people will still be there, and the stuff you've made will still be loved by a lot of them.
And it's not all bad either, in fact, the opposite is true: There are a *lot* of people that give you constructive criticism: "*I enjoyed this, but I think x, y and z could be changed and it would be even better.*" If you're the kind of person that says this, then you're a winner, because this helps not only the thing you're commenting on, but also the person that's made the thing: Now they know what you like, what you don't like, but most importantly: *How to fix it for you*. And the best thing is: You as the creator don't even have to listen to them if you disagree! They'll probably understand, because they're nice enough not to be dicks like the people I talked about before.
So keep on going, no matter what people say, because you're probably awesome.
# Basically No Obligations
When making a game, or a "real" product in general, a lot of the time, you're bound to certain obligations: Either it's legal ones, or maybe you've done a Kickstarter, maybe the thing you make actually costs you quite a bit of money because you have a whole development team, stuff like that.
Well with Minecraft modding, the only thing you have to deal with is people asking you when you'll *finally* update to the next version. But if you're sick of that, or sick of anything else, you can just stop, take a break, and do something else entirely for a while.
People might judge, but the important thing is: They don't have a right to judge, because you can do whatever you want. It's a hobby thing, so it's your thing.
# So yea,
this is all of the stuff I could think of that inspires me to keep going with Minecraft Modding. It's been a really awesome journey so far and I'm excited to keep going, to create more things and to see more things that other people created.
Thanks for reading! <3

128
commissions/index.html Normal file
View file

@ -0,0 +1,128 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ellpeck's Mod Commissions</title>
<meta name="author" content="Ellpeck">
<meta name="description" content="I accept commissions for Minecraft mods. If you want a mod of any size and complexity made, this is the place to go!">
<meta name="keywords" content="Ellpeck, Minecraft, Mods, Modding, Commission, Forge, Plugin">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
<link rel="stylesheet" href="style.css">
<link rel="icon" href="../favicon.ico">
<meta property="og:title" content="Ellpeck's Mod Commissions">
<meta property="og:description" content="I accept commissions for Minecraft mods. If you want a mod of any size and complexity made, this is the place to go!">
<meta property="og:image" content="https://www.ellpeck.de/res/logoSmall.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@Ellpeck">
<meta name="twitter:creator" content="@Ellpeck">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-150032076-2"></script>
<script>
window.dataLayer = window.dataLayer || [];
let gtag = function () {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-150032076-2');
</script>
</head>
<body>
<div class="par">
<h1>About Me</h1>
<div class="row">
<div class="col-md-auto">
<img class="rounded" width="180" height="180" src="../res/me.png">
</div>
<div class="col">
<p>Hi, I'm Ellpeck, a student and programmer from Germany. I've been making Minecraft mods for over 6 years, and during that time, I created some popular mods like <a href="https://www.curseforge.com/minecraft/mc-mods/actually-additions">Actually Additions</a> and <a href="https://www.curseforge.com/minecraft/mc-mods/natures-aura">Nature's Aura</a>. If you want to know more about my projects, you should check out <a href="https://ellpeck.de">my website</a>.</p>
<p>Recently, I started accepting commissions for mods as well. If you want a Minecraft mod of any size and complexity made, I can do that for you. ❤️</p>
</div>
</div>
</div>
<div class="par">
<h1>Past Commissions</h1>
<div class="mod-info">
<img src="media/slingshot.png" style="object-position: bottom">
<div class="mod-caption">
<h2><a href="https://www.curseforge.com/minecraft/mc-mods/slingshot">Slingshot</a></h2>
<p class="d-none d-sm-block">Slingshot allows you to shoot different items with your slingshot for different effects</p>
</div>
</div>
<div class="test test-left">
<p>A pleasant, kind, and easy to work with modder. He produces high quality work and is, in my opinion, well worth the money.</p>
<p class="test-name">Violet</p>
</div>
<div class="mod-info">
<img src="media/nyx.png" style="object-position: top">
<div class="mod-caption">
<h2><a href="https://www.curseforge.com/minecraft/mc-mods/nyx">Nyx</a></h2>
<p class="d-none d-sm-block">Nyx is a mod that transforms and improves Minecraft's time of darkness by adding elements and events themed around the moon, stars, and night sky</p>
</div>
</div>
<div class="test test-right">
Ellpeck has proven time and again to be very talented and open-minded when it comes to mod commissions. Easy and enjoyable to work with, and very patient when it comes to working out any inconsistencies. Im happy to have collaborated with him!
<p class="test-name">Drakallen</p>
</div>
<div class="mod-info">
<img src="media/enchantmentstorage.png">
<div class="mod-caption">
<h2><a href="https://www.curseforge.com/minecraft/mc-mods/enchantment-storage">Enchantment Storage</a></h2>
<p class="d-none d-sm-block">Stores and automatically converts your enchanted books into a limitless repository of knowledge</p>
</div>
</div>
<div class="test test-left">
Working with Ellpeck was an immense pleasure, he was attentive and convivial during the entire process. Upon release, I was assured any and all problems would be handled immediately, although I have yet to encounter a single issue. Brilliant work and highly recommended.
<p class="test-name">Zilch</p>
</div>
</div>
<div class="par">
<a id="terms"></a>
<h1>Terms and Pricing</h1>
<p>Here is all the information you need if you want to commission me to make a mod for you:</p>
<ul>
<li>I create Minecraft Forge mods only.</li>
<li>I work in Minecraft Versions 1.12.2 and upwards only.</li>
<li>No ports of existing mods or content.</li>
<li>No content that can't be created using "traditional" Java code (coremods).</li>
<li>You will have the choice of whether you want to receive the source code privately or whether I should publish it on GitHub.</li>
<li>You will have the choice of whether you want to be sent the mod jar or whether I should publish the mod on CurseForge. If the mod is published on CurseForge, you will get up to 50% of the share of Curse Points generated through the mod.</li>
<li>After the mod is completed, bug fixes are included for free, but feature updates and Minecraft version updates are not. They can be requested at a later date for a small fee though.</li>
<li>I charge between 10€ ($11) and 30€ ($33) an hour.
<ul>
<li>I estimate the total amount of time it will take based on the size and complexity of the mod beforehand.</li>
<li>I request half of the estimated amount to be paid before I start working on the mod, the other half once I'm finished. That way, there's security for both of us.</li>
<li>If a feature takes much more time than expected, I might raise the second payment's price slightly. Of course, if I do this, I will also provide an explanation as to why this is necessary.</li>
</ul>
</li>
<li>You have to be at least 18 years old, and has to pay with their own PayPal account. I don't accept other forms of payment.</li>
</ul>
</div>
<div class="par">
<h1>Contact</h1>
<p>If you're interested, you can easily contact me by joining my Discord server and messaging me privately from there. Alternatively, you can also <a href="mailto:me@ellpeck.de">send me an email</a>.</p>
<a href="https://ellpeck.de/discord"><img class="discord-img" src="../res/discord.png"></a>
</div>
<div class="footer">
<a href="https://git.ellpeck.de/Ellpeck/Web">&copy; 2021 Ellpeck</a> &ndash; <a href="https://ellpeck.de/#impressum">Impressum</a> &ndash; <a href="https://ellpeck.de/#privacy">Privacy</a>
</div>
</body>

View file

Before

Width:  |  Height:  |  Size: 521 KiB

After

Width:  |  Height:  |  Size: 521 KiB

View file

Before

Width:  |  Height:  |  Size: 401 KiB

After

Width:  |  Height:  |  Size: 401 KiB

View file

Before

Width:  |  Height:  |  Size: 944 KiB

After

Width:  |  Height:  |  Size: 944 KiB

View file

@ -1,6 +1,5 @@
body {
font-family: Roboto, sans-serif;
font-display: swap;
font-family: Roboto;
}
.par {
@ -24,7 +23,7 @@ body {
.mod-caption {
position: absolute;
bottom: 10px;
bottom: 8px;
color: white;
text-align: center;
left: 50%;

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
foefrenzy/docs/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

214
foefrenzy/docs/tutorial.md Normal file
View file

@ -0,0 +1,214 @@
# Creating Custom Maps
Foe Frenzy has a simple system in place that allows you to create your own maps using a simple image editing software as well as a text editor. To play a map, all you have to do is put it into a special folder and the game will automatically load it up. If you want to play a custom map in Multiplayer, you need to make sure that all players have the map installed.
## Where to Put Map Files
All files that the game uses are stored in `Documents/Foe Frenzy` on Windows or `~/Foe Frenzy` on Linux. After launching the game for the first time, a `Maps` folder and a `SpawnPools` folder will be generated. This is the location where custom maps live.
## Creating a Map
When creating a Map, two files are required:
- `Maps/MapName.xml`: This is a file containing information about the map, like which tile is which, where items can spawn, where players can spawn and so on.
Note that the example file included has some more comments inside of it that explain what the different parts of the file do.
- `Maps/Textures/MapName.png`: This file represents the actual layout of the map. You can edit it in an image editor of your choice. Each pixel represents one tile on the map, and the colors of the pixels represent what tiles should be placed there. The size of the image is the same as the size of the finished map.
Note that, further down on this page, there is some information about which colors the game's default tiles have in the map image.
Below is the file as well as the image for an example map with some comments that explain the structure in greater detail. To get started making your own map, it would be easiest to copy this information.
```xml
<?xml version="1.0"?>
<!-- XML header information, don't change this -->
<RawMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- A short description of the map that displays when it's selected to be played -->
<Description>A test custom map</Description>
<!-- The amount of seconds it takes until an item spawner spawns an item -->
<ItemSpawnerDelay>5</ItemSpawnerDelay>
<!-- The weather effect that is displayed on the map. Possible values are Snow and Rain -->
<WeatherEffect>Snow</WeatherEffect>
<!-- The type of music that should be displayed on the map. Scroll down for reference on all types -->
<MusicTheme>Lava</MusicTheme>
<!-- The map's global light modifier. This is the amount of "sunlight" that the map has, where 255 for each R, G, B value means "full light" and 0 means "no light". -->
<LightModifier>
<B>255</B>
<G>255</G>
<R>255</R>
<A>255</A>
</LightModifier>
<!-- The intensity of lights (like torches) on the map, 1 means full intensity, 0 means that they give off no light at all -->
<LightIntensity>0</LightIntensity>
<!-- The tile that is placed outside the borders of the map for visual purposes if the map is smaller than the screen size -->
<FillerTile>Grass</FillerTile>
<!-- The structure that is placed on top of the filler tile -->
<FillerStructure>Tree</FillerStructure>
<!-- Tile properties are custom combinations of tiles and structures that can be placed in specific locations using the map image. If multiple tile properties are required, an entire RawTileProperty can be copied and pasted below any number of times. -->
<TileProperties>
<RawTileProperty>
<!-- The color specified in the map image file to make this specific property occur in the map -->
<Color>
<B>151</B>
<G>255</G>
<R>151</R>
<A>255</A>
</Color>
<Tile>Grass</Tile>
<Structure>Vine</Structure>
</RawTileProperty>
</TileProperties>
<!-- This is the list of item spawners, their locations and the spawn pools they spawn from. Scroll down for reference on spawn pools. -->
<ItemSpawners>
<RawItemSpawner>
<Location>
<X>11</X>
<Y>10</Y>
</Location>
<Pool>Test</Pool>
</RawItemSpawner>
<RawItemSpawner>
<Location>
<X>25</X>
<Y>10</Y>
</Location>
<Pool>Test</Pool>
</RawItemSpawner>
</ItemSpawners>
<!-- This is the list of structure spawners. Structure spawners are basically instructions on how to randomly spawn a set of structures onto different tiles of the map. -->
<StructureSpawners>
<!-- The first spawner in this list randomly spawns Rock structures on Sand tiles. The Amount field determines how many tries the spawning should have. -->
<RawStructureSpawner>
<ValidPositions>
<string>Sand</string>
</ValidPositions>
<Structure>Rock</Structure>
<Amount>40</Amount>
</RawStructureSpawner>
<RawStructureSpawner>
<ValidPositions>
<string>Grass</string>
</ValidPositions>
<Structure>Tree</Structure>
<Amount>60</Amount>
</RawStructureSpawner>
</StructureSpawners>
<!-- These are the spawn points for the four players. When spawning, the spawn points are shuffled so that players don't spawn in the same order every time. -->
<SpawnPoints>
<Point>
<X>1</X>
<Y>8</Y>
</Point>
<Point>
<X>27</X>
<Y>4</Y>
</Point>
<Point>
<X>6</X>
<Y>18</Y>
</Point>
<Point>
<X>25</X>
<Y>15</Y>
</Point>
</SpawnPoints>
</RawMap>
```
![](docs/map.png =100%x*)
## Creating a Spawn Pool
When creating a map, the items that can spawn at any given spawn point are referenced by their Spawn Pool. A spawn pool is basically just a list of items mapped to weights, where a higher weight means that the item will spawn more often, and a lower weight means that the item will spawn less often.
Spawn pools are stored in `SpawnPools/PoolName.xml`. Note that the example file included has some more comments inside of it that explain what the different parts of the file do. Also note that, further down on this page, there is a list of the game's default spawn pools.
Below is the file for an example spawn pool with some comments that explain the structure in greater detail. To get started making your own spawn pool, it would be easiest to copy this information.
```xml
<?xml version="1.0"?>
<!-- XML header information, don't change this -->
<RawPool xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<!-- These are the entries that this spawn pool has -->
<Entries>
<!-- Each entry contains the name of an item that should be spawned, as well as the weight that item should have. Scroll down for information on what the weight value means. -->
<RawPoolEntry>
<Item>Arrow</Item>
<Weight>10</Weight>
</RawPoolEntry>
<RawPoolEntry>
<Item>Bomb</Item>
<Weight>15</Weight>
</RawPoolEntry>
<RawPoolEntry>
<Item>Sword</Item>
<Weight>15</Weight>
</RawPoolEntry>
<RawPoolEntry>
<Item>LeatherArmor</Item>
<Weight>10</Weight>
</RawPoolEntry>
<RawPoolEntry>
<Item>RubberHammer</Item>
<Weight>5</Weight>
</RawPoolEntry>
<RawPoolEntry>
<Item>Spear</Item>
<Weight>15</Weight>
</RawPoolEntry>
<RawPoolEntry>
<Item>Potion</Item>
<Weight>3</Weight>
</RawPoolEntry>
</Entries>
</RawPool>
```
## Tile Names And Colors
Each tile color is written as (R, G, B). The alpha value (A) is always 255.
- Rock (255, 255, 255)
- Grass (0, 255, 0)
- Sand (255, 255, 0)
- Water (0, 0, 255)
- DeepWater (0, 0, 96)
- Dirt (127, 51, 0)
- RockWall (0, 0, 0)
- RockWallFront (81, 81, 81)
- Lava (255, 0, 0)
- Snow (208, 219, 221)
- Ice (96, 96, 255)
- FallGrass (156, 98, 43)
## Structure Names
- Rock
- Tree (automatically changes to snow or fall tree based on the tile below)
- Torch
- Cactus
- GrassTuft (automatically generates on grass as decoration)
- Flower (automatically generates on grass as decoration)
- Vine
- JungleTree
## Item Names
- Bomb
- Sword
- Arrow
- Wall
- RubberHammer
- Pickaxe
- Dynamite
- Spear
- Potion
- FlameThrower
- LeatherArmor
- Feather
- SpeedBoots
- Torch
- Shield
- AntiSlipBoots
## Default Spawn Pools
- All (Most of the game's items)
- Low (Items that are considered lower tier)
- Cave (All items except long-range weapons like arrows)
- CaveLow (Cave items that are considered lower tier)
- Lighting (Lighting items like torches)
- Pickaxe (Items to destroy rocks with like pickaxes, bombs and dynamite)
- Ice (Most of the game's items, including flame thrower, anti-slip boots and other ice stuff)
## Music Themes
- Lava
- Beach
- Cold
- Forest
- Desert

BIN
foefrenzy/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

161
foefrenzy/index.html Normal file
View file

@ -0,0 +1,161 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Foe Frenzy</title>
<meta name="author" content="Ellpeck">
<meta name="description" content="A fast-paced fighting game where you battle up to three of your friends with random, short-lasting items">
<meta name="keywords" content="Ellpeck, Foe Frenzy, Steam, Discord, itch, itch.io, Battle, 2d, Top Down, Pixelart, MonoGame, Fighting, Game">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
<link rel="stylesheet" href="style.css">
<link rel="icon" href="favicon.ico">
<meta property="og:title" content="Foe Frenzy">
<meta property="og:description" content="A fast-paced fighting game where you battle up to three of your friends with random, short-lasting items">
<meta property="og:image" content="https://www.ellpeck.de/foefrenzy/media/logo.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@Ellpeck">
<meta name="twitter:creator" content="@Ellpeck">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-150032076-2"></script>
<script>
window.dataLayer = window.dataLayer || [];
let gtag = function () {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-150032076-2');
</script>
</head>
<body>
<div class="main rounded">
<img src="media/banner.png" class="banner rounded" alt="Foe Frenzy Logo">
<p>Foe Frenzy is a fast-paced fighting game where you battle up to three of your friends with random, short-lasting items in an attempt to be the last survivor.</p>
<h1>Get the Game</h1>
<p>You can buy Foe Frenzy for <strong>$6.99</strong> on any of the following platforms.</p>
<div class="centered">
<a class="btn btn-success store-button" role="button" href="https://store.steampowered.com/app/1194170/">
<img src="media/steam.png" class="store-img" alt="Buy on Steam">
</a>
<a class="btn btn-success store-button" role="button" href="https://discordapp.com/store/skus/606152985181028352/">
<img src="media/discord.png" class="store-img" alt="Buy on Discord">
</a>
<a class="btn btn-success store-button" role="button" href="https://ellpeck.itch.io/foefrenzy">
<img src="media/itch.png" class="store-img" alt="Buy on itch.io">
</a>
</div>
<h1>Trailer</h1>
<div class="trailer-div">
<iframe class="trailer rounded" src="https://www.youtube.com/embed/rLac7AjWo7w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
<h1>Screenshots</h1>
<div id="carousel" class="carousel slide" data-ride="carousel" data-interval="3000">
<ol class="carousel-indicators">
<li data-target="#carousel" data-slide-to="0" class="active"></li>
<li data-target="#carousel" data-slide-to="1"></li>
<li data-target="#carousel" data-slide-to="2"></li>
<li data-target="#carousel" data-slide-to="3"></li>
<li data-target="#carousel" data-slide-to="4"></li>
<li data-target="#carousel" data-slide-to="5"></li>
<li data-target="#carousel" data-slide-to="6"></li>
<li data-target="#carousel" data-slide-to="7"></li>
<li data-target="#carousel" data-slide-to="8"></li>
</ol>
<div class="carousel-inner rounded">
<div class="carousel-item active">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-40-58.png" alt="Screenshot of the main menu">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-34-56.png" alt="Screenshot of the player selection menu">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-36-36.png" alt="Screenshot of a cave map">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-39-19.png" alt="Screenshot of an ice map">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-44-47.png" alt="Screenshot of the tiebreaker screen">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-42-20.png" alt="Screenshot of a desert map">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-40-18.png" alt="Screenshot of the win screen">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-35-28.png" alt="Screenshot of the map select screen">
</div>
<div class="carousel-item">
<img class="d-block w-100 rounded" src="media/screenshots/new/05-10-2020_16-41-17.png" alt="Screenshot of the achievements screen">
</div>
</div>
<a class="carousel-control-prev" href="#carousel" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carousel" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
<h1>Features</h1>
<p>Foe Frenzy is a fast-paced fighting game where you battle up to three of your friends with random, short-lasting items in an attempt to be the last survivor.</p>
<p><img src="media/description/BattleBanner.png" alt="Battle Your Friends" width="100%"><br>Battle up to three of your friends locally or using online play. Pick up weapons, armor, healing items and more and use them to damage your enemies before they run out!</p>
<p>Play locally with up to two players on one keyboard and additional controllers, or use Steam, Discord or IP-based Multiplayer to battle over the internet!</p>
<p><img src="media/description/MapsBanner.png" alt="Play Tons of Maps" width="100%"><br>Use the terrain to your advantage on over 14 different maps. Run through hot deserts and wet swamps and chop through vines and jump down cliffs to reach the best weapons; stumble through caves where your enemies lurk in the dark and make yourself vulnerable by swimming through lakes!</p>
<p>Select the map you&#39;re best at and hope that it gets picked over the ones that your friends chose!</p>
<p><img src="media/description/ItemsBanner.png" alt="Choose Your Weapons" width="100%"><br>Fight with over 15 different items, from swords, bows and leather armor to flamethrowers and teleporters. Each item only lasts a short time, so choose carefully what move to make next!</p>
<p>Pick up random effect items that cause a disadvantage to your opponents, but have a small chance of causing you trouble too. Slow down your friends, invert their controls, or make yourself invincible!</p>
<p><img src="media/description/CustomizeBanner.png" alt="Customize" width="100%"><br>Pick the color and the look of your battler. Unlock Achievements to gain access to more character designs!</p>
<p>Once you&#39;re really good, enable expert mode for yourself for a greater challenge against your friends - lower HP, item durability and speed will make it more difficult for you to win!</p>
<p><img src="media/description/WinBanner.png" alt="Win in Time" width="100%"><br>Defeat your friends before the timer runs out to win! The results screen shows how your friends were killed, and who killed them, so make your actions count!</p>
<p>If the timer reaches a minute remaining, the game goes into Tiebreaker mode: Each player only has a single health point remaining until the end. Kill your last opponent in this exciting battle and emerge victorious!</p>
<h1>About the Project</h1>
<p>
Foe Frenzy is a game created by <a href="https://ellpeck.de">Ellpeck</a>, a student and indie game developer from Germany. It started as a small project inspired by Mario Kart and similar games, because the short-lasting battle items you can use in those games allow for fast and fun gameplay.
</p>
<p>
Development on the project started early in 2019, back when Foe Frenzy was still considered a small side project. Ellpeck made <a href="https://ellpeck.de/blog-small_projects">a blog post</a> about the process of Foe Frenzy's early creation in which he described how creating small projects is easier. After a while of developing the game, Ellpeck took a break from it due to lack of motivation. When he came back to it about two months later, he decided to turn Foe Frenzy into a bigger project, which included redesigning the art for the game, as well as the plan to turn it into a commercial project. Shortly after the rework, he made <a href="https://ellpeck.de/blog-big_projects">a second blog post</a> about this decision.
</p>
<p>With Foe Frenzy, Ellpeck hopes to have created a game that brings fun and banter to many friend groups' gaming meetups.</p>
<h1>Press</h1>
<p>
If you want to create a video or write an article about Foe Frenzy, you are more than welcome to! If you want to have it featured on this site, you can send an e-mail to <a href="mailto:me@ellpeck.de">me@ellpeck.de</a>.
</p>
<p>If you're a games journalist or a let's player and you would like to request a key for the game, you can also send an e-mail to the address above.</p>
<h2>Assets</h2>
<p>You're free to use the following images for your video or article about Foe Frenzy. Just right-click any of the images you would like to use and select <code>Save image as...</code> to save them.</p>
<div class="assets">
<img src="media/press/banner720.png" class="asset" alt="The game's banner in 16:9">
<img src="media/press/banner.png" class="asset" alt="The game's banner as a strip">
<img src="media/press/logo.png" class="asset" alt="The game's logo">
<img src="media/press/name.png" class="asset" alt="The game's logo and name">
</div>
<p><small>Note that the gray background on some of the images simply exists to make them stand out from this site's background color. It's not actually part of the images themselves.</small></p>
<p class="footer"><a href="https://git.ellpeck.de/Ellpeck/Web">&copy; 2019 Ellpeck</a> &ndash; <a href="https://ellpeck.de/#impressum">Impressum</a> &ndash; <a href="https://ellpeck.de/#privacy">Privacy</a></p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
foefrenzy/media/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
foefrenzy/media/discord.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
foefrenzy/media/itch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

BIN
foefrenzy/media/steam.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

103
foefrenzy/style.css Normal file
View file

@ -0,0 +1,103 @@
body {
position: relative;
background-image: url("media/background.png");
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
font-family: Roboto;
}
.main {
width: 50%;
display: block;
margin-top: 50px;
margin-bottom: 50px;
margin-left: auto;
margin-right: auto;
padding: 40px;
background-color: white;
}
.banner {
width: 100%;
height: auto;
margin-bottom: 20px;
}
.centered {
text-align: center;
}
.store-button {
margin: 5px;
}
.store-img {
width: 160px;
height: auto;
}
.btn {
border-radius: 0px;
}
h1 {
margin-top: 20px;
}
.trailer-div {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
}
.trailer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.assets {
text-align: center;
}
.asset {
width: 40%;
height: auto;
margin: 10px;
background-color: #BBBBBB;
}
li {
margin-top: 5px;
}
.footer {
margin-top: 40px;
}
#tutorial {
image-rendering: pixelated;
}
@media (max-width: 1200px) {
.main {
width: 80%;
}
}
@media (max-width: 768px) {
.store-img {
width: 100px;
}
}
@media (max-width: 510px) {
.main {
width: 90%;
padding: 20px;
}
}

250
index.html Normal file
View file

@ -0,0 +1,250 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ellpeck.de</title>
<meta name="author" content="Ellpeck">
<meta name="description" content="Ellpeck's little internet place">
<meta name="keywords" content="Ellpeck, Actually Additions, Rock Bottom, Programming, Minecraft, Game Development, Nature's Aura, C#, Java, Blog, Tutorial, Foe Frenzy">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">
<link rel="stylesheet" href="style/prettify.css">
<link rel="stylesheet" href="style/style.css">
<link rel="icon" href="favicon.ico">
<meta property="og:title" content="Ellpeck.de">
<meta property="og:description" content="Ellpeck's little internet place">
<meta property="og:image" content="https://www.ellpeck.de/res/logoSmall.png">
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="@Ellpeck">
<meta name="twitter:creator" content="@Ellpeck">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/prettify.js"></script>
<script src="scripts/util.js"></script>
<script src="scripts/main.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-150032076-2"></script>
<script>
window.dataLayer = window.dataLayer || [];
let gtag = function () {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-150032076-2');
</script>
<script data-ad-client="ca-pub-5754829579653773" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
</head>
<body data-spy="scroll" data-target="#navbar">
<!-- Navbar -->
<nav class="navbar fixed-top navbar-expand-lg navbar-light bg-light rounded-bottom" id="navbar">
<script src="scripts/navbar.js"></script>
<!-- Navbar brand and logo -->
<a class="navbar-brand mb-0 h1" href="#">
<img src="res/logo.png" width="40" height="40" alt=""> Ellpeck.de
</a>
<!-- Responsive navbar menu opener -->
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar-content">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Navbar content -->
<div class="collapse navbar-collapse" id="navbar-content">
<div class="navbar-nav mr-auto" id="nav-items">
<a class="nav-item nav-link" href="#projects">Projects</a>
<a class="nav-item nav-link" href="#social">Social</a>
<a class="nav-item nav-link" href="#about">About</a>
<a class="nav-item nav-link" href="#blog">Blog</a>
</div>
<span class="navbar-text">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="dark-mode">
<label class="custom-control-label" for="dark-mode">Dark Mode</label>
</div>
</span>
</div>
</nav>
<!-- Content -->
<div class="container main">
<!-- Cookie notification -->
<script src="scripts/cookieinfo.js"></script>
<div id="main">
<div class="no-blog">
<!-- Home -->
<div class="jumbotron">
<div class="container">
<div class="row">
<div class="col-md-auto">
<img src="res/me.png" class="rounded-circle" width="200" height="200" id="navbar-image">
</div>
<div class="col">
<h1 class="display-4" id="intro-text"></h1>
<script src="scripts/greet.js"></script>
<p class="lead">Welcome to my little website! I'm Ellpeck, a student and programmer from Germany. I do a lot of stuff, actually. My life is pretty busy.</p>
<!-- Sponsor buttons -->
<div class="sellout">
<iframe src="https://github.com/sponsors/Ellpeck/button" title="Sponsor Ellpeck" height="32" width="116" style="border: 0; margin-right: 10px;"></iframe>
<a href="https://www.patreon.com/bePatron?u=2494595" data-patreon-widget-type="become-patron-button"></a>
<script async src="https://c6.patreon.com/becomePatronButton.bundle.js"></script>
</div>
</div>
</div>
</div>
</div>
<!-- Projects -->
<a class="anchor" id="projects"></a>
<div class="list-display rounded">
<h1>Projects</h1>
<p>
Here is a list of some of the things that you might know me from. If you want to have a more in-depth look at everything I do, check out some of the sites linked in the <a href="#social">Social</a> section.
</p>
<div id="project-list">
<em>The content that should be here is dynamically generated. Please enable JavaScript if you see this.</em>
</div>
<script src="scripts/projects.js"></script>
</div>
<!-- Social -->
<a class="anchor" id="social"></a>
<div class="list-display rounded">
<h1>Social</h1>
<p>
These are other websites where you can find me and the things I do, including the pages where I publish my code and games and where I sometimes stream and upload videos. This list also includes a lot of ways to reach me.
</p>
<div class="row">
<div class="col">
<div id="social-list">
<em>The content that should be here is dynamically generated. Please enable JavaScript if you see this.</em>
</div>
Additionally, here are some miscellaneous platforms:
<ul>
<li>My Nintendo Switch friend code is <strong>SW-5281-8834-6801</strong></li>
<li>If you want to play my Mario Maker 2 levels, my ID is <strong>8BH-566-4WF</strong></li>
<li>If you play The Sims 4, you can check out my builds on the gallery by searching for <strong>Ellpeck</strong></li>
</ul>
</div>
<div class="col-md-auto" id="discord-div">
<em>The content that should be here is dynamically generated. Please enable JavaScript if you see this.</em>
</div>
</div>
<script src="scripts/social.js"></script>
</div>
<!-- About -->
<a class="anchor" id="about"></a>
<div class="list-display rounded">
<h1>About</h1>
<p>
Sometimes, some people ask me some questions about myself or my projects, so I decided to compile a list of some of the answers in a Q&A-like fashion so that I don't have to keep repeating them. If you're curious about me, this might be interesting to you!
</p>
<div id="about-list">
<em>The content that should be here is dynamically generated. Please enable JavaScript if you see this.</em>
</div>
<script src="scripts/about.js"></script>
</div>
<!-- Blog -->
<a class="anchor" id="blog"></a>
<div class="list-display rounded">
<h1>Blog</h1>
<p>
Occasionally I enjoy writing stuff. So here's some of the stuff I've written. Just click on any of the headers to open the post. Alternatively, you can subscribe to this blog using <a href="/rss.xml">RSS</a>, <a href="/atom.xml">Atom</a> or <a href="/feed.json">JSON</a>.
</p>
<div id="blog-list">
<em>The content that should be here is dynamically generated. Please enable JavaScript if you see this.</em>
</div>
<button type="button" class="btn btn-link" id="blog-archive-button">Show archived posts</button>
<div id="blog-archive">
<em>The content that should be here is dynamically generated. Please enable JavaScript if you see this.</em>
</div>
<script src="scripts/blog.js"></script>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="footer rounded-top">
<span class="text-muted"><a href="https://git.ellpeck.de/Ellpeck/Web">&copy; 2018-2021 Ellpeck</a> &ndash; <a href="#impressum">Impressum</a> &ndash; <a href="#privacy">Privacy</a></span>
<div class="quote">
<span id="quote-text"></span>
<script src="scripts/quote.js"></script>
<img src="res/blobheart.png" id="blobheart">
</div>
</div>
<!-- Impressum -->
<div class="modal fade" id="impressum-modal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Impressum</h5>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>
Julian Schubert
<br>Kastanienweg 27
<br>52074 Aachen
</p>
<p>
Telefon: 0241 45093753
<br>E-Mail: me@ellpeck.de
</p>
<p>Die obenstehende Person ist ebenfalls verantwortlich für den Inhalt (gem. § 55 Abs. 2 RStV).</p>
</div>
</div>
</div>
</div>
<!-- Privacy -->
<div class="modal fade" id="privacy-modal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Privacy</h5>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>This site uses cookies to store information about your browsing activity.</p>
<p>Ellpeck.de stores the following information:</p>
<ul>
<li>A cookie named <code>dark</code> with the value <code>true</code> or <code>false</code> that stores if you have dark mode enabled</li>
<li>A cookie named <code>notification</code> with the value <code>true</code> or <code>false</code> that stores if you have already closed the cookie notification that displays at the top of the page</li>
</ul>
<p>Due to widgets and embeds, additional information will be stored by other sites. Please refer to those sites' privacy policies for more information:</p>
<ul>
<li>Google uses cookies to serve ads based on a user's prior visits to this website. You may opt out of personal advertising by visiting <a href="https://www.google.com/settings/ads">Ad Settings</a>. For more information, see <a href="http://www.google.com/policies/privacy/partners/">how Google uses data when you use its partners' sites or apps</a>.</li>
<li>Discord uses cookies for its widget embed. For more information, see <a href="https://discordapp.com/privacy">their privacy policy</a>.</li>
<li>Twitter uses cookies for its tweet embeds. For more information, see <a href="https://twitter.com/en/privacy">their privacy policy</a>.</li>
</ul>
<p>
Transparency is very important to us, and as such, if you have any doubts about the security of this website, you can view its source code <a href="https://git.ellpeck.de/Ellpeck/Web">on Gitea</a>.
<br>Keep in mind that you can also review or delete stored cookies for any site at any time in your browser's settings.
</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -1,36 +0,0 @@
ExpiresActive On
ExpiresDefault A31536000
ExpiresByType text/html A600
ExpiresByType text/javascript A2592000
ExpiresByType application/javascript A2592000
Options +MultiViews -Indexes
ErrorDocument 404 /404.html
RewriteEngine On
RewriteBase /
# legacy redirects (for deleted stuff)
RewriteRule ^blog-(.*)$ "blog/$1" [R=301,L]
RewriteRule ^blog/(.*)\.html$ "blog/$1" [R=301,L]
RewriteRule ^minecraft-stuff/?$ "projects" [R=301,L]
RewriteRule ^mc/?$ "projects" [R=301,L]
RewriteRule ^press(/.*)?$ "https://press.ellpeck.de$1" [R=301,L]
RewriteRule ^tinylife/?$ "https://tinylifegame.com" [R=301,L]
RewriteRule ^foefrenzy/?$ "https://store.steampowered.com/app/1194170/" [R=301,L]
RewriteRule ^actaddchangelog/?$ "https://ell.lt/aachangelog" [R=301,L]
RewriteRule ^actadddownload/?$ "https://ell.lt/aadl" [R=301,L]
RewriteRule ^actaddlicense/?$ "https://ell.lt/aalicense" [R=301,L]
RewriteRule ^actadd/?$ "https://ell.lt/aa" [R=301,L]
RewriteRule ^discord/?$ "https://ell.lt/discord" [R=301,L]
RewriteRule ^wishlist/?$ "https://ell.lt/wishlist" [R=301,L,NE]
RewriteRule ^reedsy/?$ "https://ell.lt/reedsy" [R=301,L]
RewriteRule ^sims4gallery/?$ "https://ell.lt/s4gallery" [R=301,L,NE]
# anchor redirects
RewriteRule ^projects/?$ "#projects" [R=301,L,NE]
RewriteRule ^social/?$ "#social" [R=301,L,NE]
RewriteRule ^about/?$ "#about" [R=301,L,NE]
RewriteRule ^blog/?$ "#blog" [R=301,L,NE]
RewriteRule ^support/?$ "#support" [R=301,L,NE]
RewriteRule ^💸/?$ "#support" [R=301,L,NE]

View file

@ -1,10 +0,0 @@
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
gem "jekyll"
gem "webrick"
gem "jekyll-feed"
gem "jekyll-postfiles"

View file

@ -1,89 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.3)
public_suffix (>= 2.0.2, < 6.0)
colorator (1.1.0)
concurrent-ruby (1.2.2)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
eventmachine (1.2.7)
eventmachine (1.2.7-x64-mingw32)
ffi (1.15.5)
forwardable-extended (2.6.0)
google-protobuf (3.22.2)
http_parser.rb (0.8.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
jekyll (4.3.2)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 1.0)
jekyll-sass-converter (>= 2.0, < 4.0)
jekyll-watch (~> 2.0)
kramdown (~> 2.3, >= 2.3.1)
kramdown-parser-gfm (~> 1.0)
liquid (~> 4.0)
mercenary (>= 0.3.6, < 0.5)
pathutil (~> 0.9)
rouge (>= 3.0, < 5.0)
safe_yaml (~> 1.0)
terminal-table (>= 1.8, < 4.0)
webrick (~> 1.7)
jekyll-feed (0.17.0)
jekyll (>= 3.7, < 5.0)
jekyll-postfiles (3.1.0)
jekyll (>= 3.8.6, < 5)
jekyll-sass-converter (3.0.0)
sass-embedded (~> 1.54)
jekyll-watch (2.2.1)
listen (~> 3.0)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
liquid (4.0.4)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (5.0.1)
rake (13.1.0)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rexml (3.2.5)
rouge (4.1.0)
safe_yaml (1.0.5)
sass-embedded (1.61.0)
google-protobuf (~> 3.21)
rake (>= 10.0.0)
sass-embedded (1.61.0-x64-mingw-ucrt)
google-protobuf (~> 3.21)
sass-embedded (1.61.0-x64-mingw32)
google-protobuf (~> 3.21)
sass-embedded (1.61.0-x86_64-linux-gnu)
google-protobuf (~> 3.21)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
unicode-display_width (2.4.2)
webrick (1.8.1)
PLATFORMS
x64-mingw-ucrt
x64-mingw32
x64-unknown
x86_64-linux
DEPENDENCIES
jekyll
jekyll-feed
jekyll-postfiles
webrick
BUNDLED WITH
2.2.22

View file

@ -1,23 +0,0 @@
permalink: blog/:title/
plugins:
- jekyll-feed
- jekyll-postfiles
# markdown formatting
kramdown:
header_offset: 1
typographic_syms:
hellip: ...
mdash: ---
ndash: --
laquo: "<<"
raquo: ">>"
# atom feed information
url: https://ellpeck.de
title: Ellpeck.de
description: Ell's Little Internet Place
author: Ellpeck
feed:
posts_limit: 10000

View file

@ -1,50 +0,0 @@
[
{
"q": "What are your pronouns?",
"a": "I'm assigned male at birth and quite comfortable with that, so I go by he/him most of the time. My own gender isn't really that important to me, though, and I usually also include they/them as an option."
},
{
"q": "How old are you?",
"a": "I'm <span id=\"age\"></span> years old. This automatically updates now, too, so I won't ever forget to update it again!"
},
{
"q": "Where are you from?",
"a": "I am from Neuss, Germany and I currently live in Neu-Ulm, Germany. I lived in Aachen, Germany for a while as well."
},
{
"q": "Why are you called Ellpeck?",
"a": "Well, it actually isn't as interesting of a story as some of you might hope. Long story short, when I was little (and was, apparently, very bad at English), I decided to make a YouTube channel called \"LetsPlayEveryGames.\" Shortly after, I also made a Minecraft account that I was going to call the same thing. At the time, though, there was a limit for how many characters your name could have, and so I opted for calling myself \"LPEG\" instead. When a friend of mine came along and started trying to pronounce that name, instead of saying each individual letter on its own, he started pronouncing it like a word: Ell-Peg. ...Ellpeck. I liked that pronounciation and so I stuck with the name."
},
{
"q": "What should I call you?",
"a": "In general, most of my friends online call me Ell nowadays, which is also what I go by on Twitter and Discord in terms of my display names. My boyfriend calls me Peck online, but I generally don't like it when other people do so. If we meet in real life, you can call me Ell or Julian, the latter of which is my real name."
},
{
"q": "What languages do you speak?",
"a": "I speak German, English, Java and C# fluently. I'm okay at JavaScript, TypeScript, Python and PHP, and I can just about get something to happen in C and Lua."
},
{
"q": "How do you make games?",
"a": "I usually use the .NET-based framework <a href=\"https://www.monogame.net/\">MonoGame</a> together with some libraries to make it a bit easier, including my own library, MLEM, which you can read about in the <a href=\"#projects\">Projects</a> section."
},
{
"q": "When are you updating your Minecraft mods?",
"a": "If you're asking about Actually Additions, <a href=\"https://ellpeck.de/blog/future_actually_additions\">I don't work on it anymore</a>. If you're asking about any of my other mods: Don't. I'll do it when I feel like it."
},
{
"q": "What's your job/occupation?",
"a": "I'm currently studying Computer Science at <a href=\"https://www.uni-ulm.de/en/\">Ulm University</a>. I don't have a job on the side or anything, though."
},
{
"q": "Are you for hire?",
"a": "You can commission me to make a Minecraft mod for you. You can find more information on my <a href=\"https://ellpeck.de/commissions\">commissions page</a>. For other work, you can check out my page on <a href=\"https://www.linkedin.com/in/ellpeck/\">LinkedIn</a>."
},
{
"q": "What do you use to code?",
"a": "For Java, I use IntelliJ IDEA. For C#, I use JetBrains Rider. For anything web-related, I use PhpStorm. For most of my other projects, which are usually rather small, I use Visual Studio Code."
},
{
"q": "What's your favorite programming language?",
"a": "C#."
}
]

View file

@ -1,157 +0,0 @@
[
{
"name": "🏡 Tiny Life",
"desc": "Tiny Life is a fun simulation game that tries to capture the essence of games like The Sims, but in an isometric pixelart style. It's published by Top Hat Studios, and it's currently in Steam Early Access.",
"status": "In development",
"links": [
{
"name": "Check it out",
"link": "https://tinylifegame.com"
},
{
"name": "Get it on Steam",
"link": "https://store.steampowered.com/app/1651490/Tiny_Life/"
}
],
"icon": "tiny"
},
{
"name": "💡 Actually Additions",
"desc": "Actually Additions is a rather popular Minecraft mod that I used to work on. It's become widely known in the modding community and has reached over 30 million downloads by now, which is crazy. I don't work on it anymore myself, but it's being maintained for current versions by someone else.",
"links": [
{
"name": "CurseForge page",
"link": "https://ellpeck.de/actadd"
},
{
"name": "Report an issue",
"link": "https://github.com/Ellpeck/ActuallyAdditions/issues"
},
{
"name": "Online manual",
"link": "https://ellpeck.de/actaddmanual/"
}
],
"status": "Maintained",
"icon": "aa"
},
{
"name": "🌲 Nature's Aura",
"desc": "Nature's Aura is a Minecraft mod about collecting, using and replenishing the Aura naturally present in the world to create useful devices and unique mechanics.",
"status": "Side project",
"links": [
{
"name": "CurseForge page",
"link": "https://minecraft.curseforge.com/projects/natures-aura"
},
{
"name": "Modrinth page",
"link": "https://modrinth.com/mod/natures-aura"
},
{
"name": "Report an issue",
"link": "https://github.com/Ellpeck/NaturesAura/issues"
}
],
"icon": "na"
},
{
"name": "🚋 Pretty Pipes",
"desc": "Pretty Pipes is a simple to use, all-inclusive item transport mod for Minecraft. It features simple pipes that can be upgraded using modules to accomplish much more advanced tasks.",
"status": "Side project",
"links": [
{
"name": "CurseForge page",
"link": "https://www.curseforge.com/minecraft/mc-mods/pretty-pipes"
},
{
"name": "Modrinth page",
"link": "https://modrinth.com/mod/pretty-pipes"
},
{
"name": "Report an issue",
"link": "https://github.com/Ellpeck/PrettyPipes/issues"
}
],
"icon": "pp"
},
{
"name": "🖼️ Custom Frames",
"desc": "Custom Frames is an <a href=\"https://obsidian.md/\">Obsidian</a> plugin that turns web apps into panes using iframes with custom styling. Also comes with presets for Google Keep and more.",
"links": [
{
"name": "Check it out",
"link": "https://github.com/Ellpeck/ObsidianCustomFrames"
},
{
"name": "Install it",
"link": "https://obsidian.md/plugins?id=obsidian-custom-frames"
}
],
"status": "Side project",
"icon": "obsidian"
},
{
"name": "⏱️ Super Simple Time Tracker",
"desc": "Super Simple Time Tracker is an <a href=\"https://obsidian.md/\">Obsidian</a> plugin that adds multi-purpose, easy-to-use time trackers to your notes that continue running even when you close Obsidian.",
"links": [
{
"name": "Check it out",
"link": "https://github.com/Ellpeck/ObsidianSimpleTimeTracker"
},
{
"name": "Install it",
"link": "https://obsidian.md/plugins?id=simple-time-tracker"
}
],
"status": "Side project",
"icon": "obsidian"
},
{
"name": "🕹️ MLEM",
"desc": "MLEM Library for Extending MonoGame is an addition to the game framework <a href=\"https://www.monogame.net/\">MonoGame</a> that provides extension methods, quality of life improvements and additional features like a Ui system and easy input handling.",
"links": [
{
"name": "Get it on NuGet",
"link": "https://www.nuget.org/packages?q=ellpeck+mlem"
},
{
"name": "See the website",
"link": "https://mlem.ellpeck.de/"
}
],
"status": "Side project",
"icon": "mlem"
},
{
"name": "🎟️ Touchy Tickets",
"desc": "Touchy Tickets is a fun idle game for Android that has you selling tickets with various theme park attractions. You can download it for free on the Google Play Store, as well as see its public source code.",
"links": [
{
"name": "Google Play Store",
"link": "https://ell.lt/touchytickets"
},
{
"name": "See the code",
"link": "https://git.ellpeck.de/Ellpeck/TouchyTickets"
}
],
"status": "Released",
"icon": "tt"
},
{
"name": "⚔️ Foe Frenzy",
"desc": "Foe Frenzy is a small party game where you battle up to three of your friends with random, short-lasting items. I initially released this game as local-only right before the pandemic, so it never had a chance to become popular. I'm still quite fond of it though, and you can buy it for a few bucks and even play online now, too.",
"links": [
{
"name": "Steam",
"link": "https://store.steampowered.com/app/1194170/Foe_Frenzy/"
}, {
"name": "itch.io",
"link": "https://ellpeck.itch.io/foefrenzy"
}
],
"status": "Released",
"icon": "ff"
}
]

View file

@ -1,45 +0,0 @@
[
{
"name": "Twitter",
"link": "https://twitter.com/Ellpeck"
},
{
"name": "Bluesky",
"link": "https://bsky.app/profile/ellpeck.bsky.social"
},
{
"name": "GitHub",
"link": "https://github.com/Ellpeck/",
"darkIcon": true
},
{
"name": "Forgejo",
"link": "https://git.ellpeck.de/Ellpeck"
},
{
"name": "itch.io",
"link": "https://ellpeck.itch.io/",
"darkIcon": true
},
{
"name": "NuGet",
"link": "https://www.nuget.org/profiles/Ellpeck"
},
{
"name": "Twitch",
"link": "https://twitch.tv/ellpeck"
},
{
"name": "YouTube",
"link": "https://www.youtube.com/c/ellpeck"
},
{
"name": "Instagram",
"link": "https://instagram.com/Ellopecko"
},
{
"name": "Email",
"link": "mailto:me@ellpeck.de",
"darkIcon": true
}
]

View file

@ -1,22 +0,0 @@
[
{
"name": "🪙 Buy Stuff I Made",
"text": "Some of the stuff I make is directly for sale, and buying it is an easy way to support me! For example, you can buy my game Tiny Life on Steam.",
"link": "#projects"
},
{
"name": "☕ Buy me a Coffee",
"text": "Ko-fi is a great site that allows you to send me just enough money to buy a coffee. And I love those. You can also use it to send monthly payments, but the other sites in this list are usually better for that.",
"link": "https://ko-fi.com/ellpeck"
},
{
"name": "⭐ Become a Patron",
"text": "Patreon allows you to support me on a monthly basis with an amount of money of your choosing. You can also get fun rewards, like player accessories to impress your friends when using my Minecraft mods!",
"link": "https://patreon.com/Ellpeck"
},
{
"name": "💻 Sponsor on GitHub",
"text": "GitHub Sponsors is basically Patreon for programmers, and it provides a monthly support option, but it also allows you to make one-time payments. Here, you can get the same rewards as you get on Patreon.",
"link": "https://github.com/sponsors/Ellpeck"
}
]

View file

@ -1,20 +0,0 @@
<div class="list-display rounded">
<h1 id="about">💬 About</h1>
<p>
Sometimes, some people ask me some questions about myself or my projects, so I decided to compile a list of some of the answers in a Q&A-like fashion so that I don't have to keep repeating them. If you're curious about me, this might be interesting to you!
</p>
<div id="about-list">
{% for item in site.data.about %}
<p>
<strong>Q: {{ item.q }}</strong><br>
<strong>A:</strong> {{ item.a }}
</p>
{% endfor %}
</div>
<script>
let birthdayMillis = Date.UTC(1999, 5 - 1, 21);
let todayMillis = Date.now();
let ageSinceStart = new Date(todayMillis - birthdayMillis);
$("#age").html(ageSinceStart.getUTCFullYear() - 1970);
</script>
</div>

Some files were not shown because too many files have changed in this diff Show more