Skip to content

Commit 0198081

Browse files
committed
[ADD] Chapter 1: Build a Clicker game - points 1 to 10
[ADD] Chapter 1: Build a Clicker game - points 11 to 21
1 parent 95317a8 commit 0198081

File tree

14 files changed

+588
-0
lines changed

14 files changed

+588
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
export const rewards = [
2+
{
3+
description: "Get 1 click bot",
4+
apply(clicker) {
5+
clicker.bots["clickBots"].number += 1;
6+
},
7+
maxLevel: 2,
8+
},
9+
{
10+
description: "Get 10 click bot",
11+
apply(clicker) {
12+
clicker.bots["clickBots"].number += 10;
13+
},
14+
minLevel: 1,
15+
maxLevel: 3,
16+
},
17+
{
18+
description: "Get 5 big bot",
19+
apply(clicker) {
20+
clicker.bots["bigBots"].number += 5;
21+
},
22+
minLevel: 2,
23+
maxLevel: 4,
24+
},
25+
{
26+
description: "Increase bot power!",
27+
apply(clicker) {
28+
clicker.power += 1;
29+
},
30+
minLevel: 3,
31+
},
32+
{
33+
description: "Increase bot power by 100!",
34+
apply(clicker) {
35+
clicker.power += 100;
36+
},
37+
minLevel: 4,
38+
},
39+
{
40+
description: "Get a Cherry Tree",
41+
apply(clicker) {
42+
clicker.trees["cherryTrees"].number += 1;
43+
},
44+
minLevel: 4,
45+
},
46+
{
47+
description: "Get a Pear Tree",
48+
apply(clicker) {
49+
clicker.trees["pearTrees"].number += 1;
50+
},
51+
minLevel: 4,
52+
},
53+
54+
];
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Component } from "@odoo/owl";
2+
import { humanNumber } from "@web/core/utils/numbers";
3+
4+
export class ClickValue extends Component {
5+
static template = "awesome_clicker.ClickValue";
6+
7+
static props = {
8+
value: Number
9+
}
10+
11+
get display() {
12+
return humanNumber(this.props.value, { decimals: 1, })
13+
}
14+
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<templates xml:space="preserve">
3+
4+
<t t-name="awesome_clicker.ClickValue">
5+
6+
<span t-att-data-tooltip="this.props.value"><t t-esc="display"/></span>
7+
8+
</t>
9+
10+
</templates>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { useState } from "@odoo/owl";
2+
import { useService } from "@web/core/utils/hooks";
3+
4+
export function useClicker() {
5+
return useState(useService("awesome_clicker.clicker"));
6+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
export const CURRENT_VERSION = 2.0;
2+
export const migrations = [
3+
{
4+
fromVersion: 1.0,
5+
toVersion: 1.5,
6+
apply: (state) => {
7+
console.log("Nothing to do, you are uptodate dude!");
8+
}
9+
},
10+
{
11+
fromVersion: 1.5,
12+
toVersion: 2.0,
13+
apply: (state) => {
14+
console.log("New tree available: peach tree!");
15+
state.trees.peachTree = {
16+
price: 1000000,
17+
number: 0,
18+
level: 4,
19+
fruit: "peaches"
20+
}
21+
state.fruits.peaches = 0;
22+
}
23+
}
24+
];
25+
26+
export function migrate(localState) {
27+
if (localState?.version_number < CURRENT_VERSION) {
28+
for (const migration of migrations) {
29+
if (localState.version_number === migration.fromVersion) {
30+
migration.apply(localState);
31+
localState.version_number = migration.toVersion
32+
}
33+
}
34+
localState.version_number = CURRENT_VERSION;
35+
}
36+
return localState;
37+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
2+
import { Reactive } from "@web/core/utils/reactive";
3+
import { EventBus } from "@odoo/owl";
4+
import { rewards } from "./click_rewards";
5+
import { randomChoice } from "./utils";
6+
import { CURRENT_VERSION } from "./clicker_migration";
7+
8+
export class ClickerModel extends Reactive {
9+
10+
constructor() {
11+
super();
12+
this.value = 0;
13+
this.level = 0;
14+
this.bus = new EventBus();
15+
this.bots = {
16+
clickBots : {
17+
price: 1000,
18+
number: 0,
19+
level: 1,
20+
clicks: 10
21+
},
22+
bigBots : {
23+
price: 5000,
24+
number: 0,
25+
level: 2,
26+
clicks: 100
27+
}
28+
}
29+
this.power = 1;
30+
this.fruits = {
31+
cherries: 0,
32+
pears: 0,
33+
peaches: 0
34+
};
35+
this.trees = {
36+
cherryTrees: {
37+
price: 1000000,
38+
number: 0,
39+
level: 4,
40+
fruit: "cherries"
41+
},
42+
pearTrees: {
43+
price: 1000000,
44+
number: 0,
45+
level: 4,
46+
fruit: "pears"
47+
},
48+
peachTree: {
49+
price: 1500000,
50+
level: 4,
51+
produce: "peaches",
52+
purchased: 0,
53+
}
54+
};
55+
this.version_number = CURRENT_VERSION;
56+
}
57+
58+
increment(inc) {
59+
this.value += inc;
60+
if (this.milestones[this.level] && this.value >= this.milestones[this.level].clicks) {
61+
this.bus.trigger("MILESTONE", this.milestones[this.level]);
62+
this.level++;
63+
}
64+
}
65+
66+
buyBot(botName) {
67+
const bot = this.bots[botName];
68+
const clickBotPrice = bot.price;
69+
if (this.value < clickBotPrice) {
70+
return false;
71+
}
72+
bot.number++;
73+
this.value-=clickBotPrice;
74+
};
75+
76+
clickBotsAction() {
77+
for(const bot in this.bots) {
78+
this.value += ( this.bots[bot].clicks * this.bots[bot].number ) * this.power;
79+
}
80+
};
81+
82+
buyPower() {
83+
const powerPrice = 50000;
84+
if (this.value < powerPrice) {
85+
return false;
86+
}
87+
this.power++;
88+
this.value-=powerPrice;
89+
};
90+
91+
getReward() {
92+
const availableReward = [];
93+
for (const reward of rewards) {
94+
if (reward.minLevel <= this.level || !reward.minLevel) {
95+
if (reward.maxLevel >= this.level || !reward.maxLevel) {
96+
availableReward.push(reward);
97+
}
98+
}
99+
}
100+
const reward = randomChoice(availableReward);
101+
this.bus.trigger("REWARD", reward);
102+
return reward;
103+
}
104+
105+
buyTree(treeName) {
106+
const tree = this.trees[treeName];
107+
const treePrice = tree.price;
108+
if (this.value < treePrice) {
109+
return false;
110+
}
111+
tree.number++;
112+
this.value-=treePrice;
113+
};
114+
115+
treesAction() {
116+
for(const tree in this.trees) {
117+
this.fruits[this.trees[tree].fruit] += this.trees[tree].number;
118+
}
119+
};
120+
121+
toJSON() {
122+
const json = Object.assign({}, this);
123+
delete json["bus"];
124+
return json;
125+
}
126+
127+
static fromJSON(json) {
128+
const clicker = new ClickerModel();
129+
const clickerInstance = Object.assign(clicker, json);
130+
return clickerInstance;
131+
}
132+
133+
get milestones() {
134+
return [
135+
{ clicks: 1000, bot: "ClickBot" },
136+
{ clicks: 5000, bot: "BigBot" },
137+
{ clicks: 100000, bot: "Power" },
138+
{ clicks: 1000000, bot: "Trees and Fruits" }
139+
]
140+
}
141+
142+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { registry } from "@web/core/registry";
2+
3+
const commandProviderRegistry = registry.category("command_provider");
4+
5+
commandProviderRegistry.add("clicker", {
6+
provide: (env, options) => {
7+
return [
8+
{
9+
name: "Open Clicker Game",
10+
action() {
11+
env.services.action.doAction({
12+
type: "ir.actions.client",
13+
tag: "awesome_clicker.ClientAction",
14+
target: "new",
15+
name: "Clicker Game"
16+
});
17+
}
18+
},
19+
{
20+
name: "Buy 1 click bot",
21+
action() {
22+
env.services["awesome_clicker.clicker"].buyBot("clickBots");
23+
}
24+
}
25+
]
26+
}
27+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { registry } from "@web/core/registry";
2+
import { ClickerModel } from "./clicker_model";
3+
import { browser } from "@web/core/browser/browser";
4+
import { migrate } from "./clicker_migration";
5+
6+
const clickerService = {
7+
dependencies: ["action", "effect", "notification"],
8+
start(env, services) {
9+
const localState = migrate(JSON.parse(browser.localStorage.getItem("clickerState")));
10+
const model = localState ? ClickerModel.fromJSON(localState): new ClickerModel();
11+
12+
document.addEventListener("click", () => model.increment(1), true);
13+
14+
setInterval(() => model.clickBotsAction(), 10000);
15+
setInterval(() => model.treesAction(), 30000);
16+
setInterval(() => {
17+
browser.localStorage.setItem("clickerState", JSON.stringify(model))
18+
}, 10000);
19+
20+
const bus = model.bus;
21+
bus.addEventListener("MILESTONE", (ev) => {
22+
services.effect.add({
23+
message: `Milestone reached! You can now buy ${ev.detail.bot}`,
24+
type: "rainbow_man",
25+
});
26+
});
27+
28+
bus.addEventListener("REWARD", (ev) => {
29+
const closeNotification = services.notification.add(
30+
`Congrats you won a reward: "${ev.detail.description}"`,
31+
{
32+
type: "success",
33+
sticky: true,
34+
buttons: [
35+
{
36+
name: "Collect",
37+
onClick: () => {
38+
ev.detail.apply(model);
39+
closeNotification();
40+
services.action.doAction({
41+
type: "ir.actions.client",
42+
tag: "awesome_clicker.ClientAction",
43+
target: "new",
44+
name: "Clicker Game"
45+
});
46+
},
47+
},
48+
],
49+
}
50+
);
51+
});
52+
53+
return model
54+
}
55+
}
56+
57+
registry.category("services").add("awesome_clicker.clicker", clickerService);

0 commit comments

Comments
 (0)