From 34c65728d05a132e3f8187ff3b46f69fb49b7081 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Wed, 24 Dec 2025 13:08:21 +0100 Subject: [PATCH 01/20] [ADD] awesome_clicker: add clicker in systray --- .../static/src/clicker_menu/clicker_menu.js | 20 +++++++++++++++++++ .../static/src/clicker_menu/clicker_menu.xml | 13 ++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 awesome_clicker/static/src/clicker_menu/clicker_menu.js create mode 100644 awesome_clicker/static/src/clicker_menu/clicker_menu.xml diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js new file mode 100644 index 00000000000..92b7530b250 --- /dev/null +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -0,0 +1,20 @@ +import { Component, useState } from "@odoo/owl"; +import { registry } from "@web/core/registry"; + +export class ClickerMenu extends Component { + static template = "awesome_clicker.clicker_menu"; + + setup() { + this.state = useState({ value: 0 }); + } + + increment() { + this.state.value++; + } +} + +export const systrayItem = { + Component: ClickerMenu, +}; + +registry.category("systray").add("awesome_clicker.clicker_menu", systrayItem); diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml new file mode 100644 index 00000000000..b82637c49ab --- /dev/null +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -0,0 +1,13 @@ + + + + +
+ Clicks: +
+ +
+ +
From 2004e936cca019680cf27b8cfd08ce00bc49e857 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Wed, 24 Dec 2025 13:23:06 +0100 Subject: [PATCH 02/20] [IMP] awesome_clicker: count external clicks --- awesome_clicker/static/src/clicker_menu/clicker_menu.js | 7 ++++--- awesome_clicker/static/src/clicker_menu/clicker_menu.xml | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index 92b7530b250..12a27953c35 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -1,4 +1,4 @@ -import { Component, useState } from "@odoo/owl"; +import { Component, useExternalListener, useState } from "@odoo/owl"; import { registry } from "@web/core/registry"; export class ClickerMenu extends Component { @@ -6,10 +6,11 @@ export class ClickerMenu extends Component { setup() { this.state = useState({ value: 0 }); + useExternalListener(document.body, "click", () => this.incrementState(this.state, 1)); } - increment() { - this.state.value++; + incrementState(state, val) { + state.value += val; } } diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index b82637c49ab..dc7705ae0e4 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -6,7 +6,7 @@ Clicks: From 6eaaa5d03ad77debc7978f2ac5a7e9e71369c5de Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Wed, 24 Dec 2025 13:53:37 +0100 Subject: [PATCH 03/20] [IMP] awesome_clicker: add clicker action --- .../static/src/clicker_action/clicker_action.js | 8 ++++++++ .../static/src/clicker_action/clicker_action.xml | 8 ++++++++ .../static/src/clicker_menu/clicker_menu.js | 11 +++++++++++ .../static/src/clicker_menu/clicker_menu.xml | 1 + 4 files changed, 28 insertions(+) create mode 100644 awesome_clicker/static/src/clicker_action/clicker_action.js create mode 100644 awesome_clicker/static/src/clicker_action/clicker_action.xml diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.js b/awesome_clicker/static/src/clicker_action/clicker_action.js new file mode 100644 index 00000000000..7cc315bb560 --- /dev/null +++ b/awesome_clicker/static/src/clicker_action/clicker_action.js @@ -0,0 +1,8 @@ +import { Component } from "@odoo/owl"; +import { registry } from "@web/core/registry"; + +export class ClickerAction extends Component { + static template = "awesome_clicker.clicker_action"; +} + +registry.category("actions").add("awesome_clicker.clicker_action", ClickerAction); diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml new file mode 100644 index 00000000000..071860c4ded --- /dev/null +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -0,0 +1,8 @@ + + + + + Hello world! + + + diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index 12a27953c35..8703d6e6d61 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -1,17 +1,28 @@ import { Component, useExternalListener, useState } from "@odoo/owl"; import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; export class ClickerMenu extends Component { static template = "awesome_clicker.clicker_menu"; setup() { this.state = useState({ value: 0 }); + this.action = useService("action"); useExternalListener(document.body, "click", () => this.incrementState(this.state, 1)); } incrementState(state, val) { state.value += val; } + + doAction() { + this.action.doAction({ + type: "ir.actions.client", + tag: "awesome_clicker.clicker_action", + target: "new", + name: "Clicker Game", + }); + } } export const systrayItem = { diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index dc7705ae0e4..a64f44dc264 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -8,6 +8,7 @@ + From 7c08dc6c85e15a7f2d17b92f75a020806afb20a2 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Wed, 24 Dec 2025 14:33:16 +0100 Subject: [PATCH 04/20] [IMP] awesome_clicker: move the clicker state to a service --- .../static/src/clicker_action/clicker_action.js | 8 +++++++- .../src/clicker_action/clicker_action.xml | 3 ++- .../static/src/clicker_menu/clicker_menu.js | 9 +++------ .../static/src/clicker_menu/clicker_menu.xml | 5 +---- awesome_clicker/static/src/clicker_service.js | 17 +++++++++++++++++ 5 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 awesome_clicker/static/src/clicker_service.js diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.js b/awesome_clicker/static/src/clicker_action/clicker_action.js index 7cc315bb560..380d8e4de99 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.js +++ b/awesome_clicker/static/src/clicker_action/clicker_action.js @@ -1,8 +1,14 @@ -import { Component } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; export class ClickerAction extends Component { static template = "awesome_clicker.clicker_action"; + + setup() { + this.clickerService = useService("awesome_clicker.game_service"); + this.clickerState = useState(this.clickerService.state); + } } registry.category("actions").add("awesome_clicker.clicker_action", ClickerAction); diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 071860c4ded..213830230ad 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -2,7 +2,8 @@ - Hello world! + Clicks: + diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index 8703d6e6d61..e8af633b69f 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -6,13 +6,10 @@ export class ClickerMenu extends Component { static template = "awesome_clicker.clicker_menu"; setup() { - this.state = useState({ value: 0 }); + this.clickerService = useService("awesome_clicker.game_service"); + this.clickerState = useState(this.clickerService.state); this.action = useService("action"); - useExternalListener(document.body, "click", () => this.incrementState(this.state, 1)); - } - - incrementState(state, val) { - state.value += val; + useExternalListener(document.body, "click", () => this.clickerService.increment(1)); } doAction() { diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index a64f44dc264..2569a8f1181 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -3,11 +3,8 @@
- Clicks: + Clicks:
-
diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js new file mode 100644 index 00000000000..a881efe26e7 --- /dev/null +++ b/awesome_clicker/static/src/clicker_service.js @@ -0,0 +1,17 @@ +import { reactive } from "@odoo/owl"; +import { registry } from "@web/core/registry"; + +export const clickerService = { + start() { + const state = reactive({ clicks: 0 }); + + return { + state, + increment(val) { + state.clicks += val; + }, + }; + }, +}; + +registry.category("services").add("awesome_clicker.game_service", clickerService); From 1e71debade476e8ead76625fdf690fbf6ade9251 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Wed, 24 Dec 2025 14:50:00 +0100 Subject: [PATCH 05/20] [IMP] awesome_clicker: add a "useClicker" hook --- .../static/src/clicker_action/clicker_action.js | 7 +++---- .../static/src/clicker_action/clicker_action.xml | 4 ++-- .../static/src/clicker_menu/clicker_menu.js | 8 ++++---- .../static/src/clicker_menu/clicker_menu.xml | 2 +- awesome_clicker/static/src/utils.js | 12 ++++++++++++ 5 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 awesome_clicker/static/src/utils.js diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.js b/awesome_clicker/static/src/clicker_action/clicker_action.js index 380d8e4de99..f8b66e2ce52 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.js +++ b/awesome_clicker/static/src/clicker_action/clicker_action.js @@ -1,13 +1,12 @@ -import { Component, useState } from "@odoo/owl"; +import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; -import { useService } from "@web/core/utils/hooks"; +import { useClicker } from "../utils"; export class ClickerAction extends Component { static template = "awesome_clicker.clicker_action"; setup() { - this.clickerService = useService("awesome_clicker.game_service"); - this.clickerState = useState(this.clickerService.state); + this.clicker = useClicker(); } } diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 213830230ad..79c6966867f 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -2,8 +2,8 @@ - Clicks: - + Clicks: + diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index e8af633b69f..34ce5367234 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -1,15 +1,15 @@ -import { Component, useExternalListener, useState } from "@odoo/owl"; +import { Component, useExternalListener } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; +import { useClicker } from "../utils"; export class ClickerMenu extends Component { static template = "awesome_clicker.clicker_menu"; setup() { - this.clickerService = useService("awesome_clicker.game_service"); - this.clickerState = useState(this.clickerService.state); + this.clicker = useClicker(); + useExternalListener(document.body, "click", () => this.clicker.increment(1)); this.action = useService("action"); - useExternalListener(document.body, "click", () => this.clickerService.increment(1)); } doAction() { diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index 2569a8f1181..173690b52ff 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -3,7 +3,7 @@
- Clicks: + Clicks:
diff --git a/awesome_clicker/static/src/utils.js b/awesome_clicker/static/src/utils.js new file mode 100644 index 00000000000..ef14a9dc765 --- /dev/null +++ b/awesome_clicker/static/src/utils.js @@ -0,0 +1,12 @@ +import { useState } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; + +export function useClicker() { + const service = useService("awesome_clicker.game_service"); + const state = useState(service.state); + + return { + ...service, + state, + }; +} From d89ddddd6708bf8458b9261e48d9f33e90c4a480 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Wed, 24 Dec 2025 16:01:29 +0100 Subject: [PATCH 06/20] [IMP] awesome_clicker: humanize display of click numbers --- awesome_clicker/static/src/click_value.js | 17 +++++++++++++++++ .../static/src/clicker_action/clicker_action.js | 2 ++ .../src/clicker_action/clicker_action.xml | 4 ++-- .../static/src/clicker_menu/clicker_menu.js | 2 ++ .../static/src/clicker_menu/clicker_menu.xml | 2 +- 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 awesome_clicker/static/src/click_value.js diff --git a/awesome_clicker/static/src/click_value.js b/awesome_clicker/static/src/click_value.js new file mode 100644 index 00000000000..e4b63f33e55 --- /dev/null +++ b/awesome_clicker/static/src/click_value.js @@ -0,0 +1,17 @@ +import { Component, xml } from "@odoo/owl"; +import { humanNumber } from "@web/core/utils/numbers"; + +export class ClickValue extends Component { + static props = { + value: Number, + }; + static template = xml``; + + format(value) { + if (value < 1000) { + return value; + } else { + return humanNumber(value, { minDigits: 0, decimals: 1 }); + } + } +} diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.js b/awesome_clicker/static/src/clicker_action/clicker_action.js index f8b66e2ce52..06167154e0e 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.js +++ b/awesome_clicker/static/src/clicker_action/clicker_action.js @@ -1,9 +1,11 @@ import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { useClicker } from "../utils"; +import { ClickValue } from "../click_value"; export class ClickerAction extends Component { static template = "awesome_clicker.clicker_action"; + static components = { ClickValue }; setup() { this.clicker = useClicker(); diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 79c6966867f..a010a9bf094 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -2,8 +2,8 @@ - Clicks: - + Clicks: + diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index 34ce5367234..4e3db191e5f 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -2,9 +2,11 @@ import { Component, useExternalListener } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; import { useClicker } from "../utils"; +import { ClickValue } from "../click_value"; export class ClickerMenu extends Component { static template = "awesome_clicker.clicker_menu"; + static components = { ClickValue }; setup() { this.clicker = useClicker(); diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index 173690b52ff..26f2ae9118d 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -3,7 +3,7 @@
- Clicks: + Clicks:
From cefa856c88bcd7258d8992977adfecf44a55ea14 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Wed, 24 Dec 2025 16:11:35 +0100 Subject: [PATCH 07/20] [IMP] awesome_clicker: add tooltip to ClickValue component --- awesome_clicker/static/src/click_value.js | 8 +++++++- .../static/src/clicker_action/clicker_action.xml | 4 ++-- awesome_clicker/static/src/clicker_menu/clicker_menu.xml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/awesome_clicker/static/src/click_value.js b/awesome_clicker/static/src/click_value.js index e4b63f33e55..65b89be5cfb 100644 --- a/awesome_clicker/static/src/click_value.js +++ b/awesome_clicker/static/src/click_value.js @@ -3,9 +3,15 @@ import { humanNumber } from "@web/core/utils/numbers"; export class ClickValue extends Component { static props = { + label: { type: String, optional: true }, value: Number, }; - static template = xml``; + static template = xml` + + + + + `; format(value) { if (value < 1000) { diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index a010a9bf094..6eeef4d51f3 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -2,8 +2,8 @@ - Clicks: - + + diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index 26f2ae9118d..990e78bc6e1 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -3,7 +3,7 @@
- Clicks: +
From 675acf02f8d96dffc3f1652ac63b77d554e80312 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 09:05:42 +0100 Subject: [PATCH 08/20] [IMP] awesome_clicker: add clickbots - Add clickbots - Move event registration for clicks into the clicker_service + set useCapture to true --- .../src/clicker_action/clicker_action.xml | 17 ++++++++-- .../static/src/clicker_menu/clicker_menu.js | 3 +- awesome_clicker/static/src/clicker_service.js | 34 ++++++++++++++++--- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 6eeef4d51f3..a3bf412f625 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -2,8 +2,21 @@ - - +
+ + +
+
+
Bots
+
+
+ 1x ClickBots (10 clicks/10seconds) +
+
+ +
+
+
diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index 4e3db191e5f..101f38907e8 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -1,4 +1,4 @@ -import { Component, useExternalListener } from "@odoo/owl"; +import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; import { useClicker } from "../utils"; @@ -10,7 +10,6 @@ export class ClickerMenu extends Component { setup() { this.clicker = useClicker(); - useExternalListener(document.body, "click", () => this.clicker.increment(1)); this.action = useService("action"); } diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index a881efe26e7..fc9553b7157 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -3,13 +3,39 @@ import { registry } from "@web/core/registry"; export const clickerService = { start() { - const state = reactive({ clicks: 0 }); + const state = reactive({ + clicks: 0, + level: 0, + clickBots: 0, + }); + function increment(val) { + state.clicks += val; + if (state.level < 1 && state.clicks >= 1000) { + state.level++; + } + } + + function buyClickBot() { + const botPrice = 1000; + if (state.clicks < botPrice) { + return; + } + + state.clicks -= botPrice; + state.clickBots++; + } + + function botsDoClicks() { + state.clicks += state.clickBots * 10; + } + + document.addEventListener("click", () => increment(1), true); + setInterval(botsDoClicks, 10000); return { state, - increment(val) { - state.clicks += val; - }, + increment, + buyClickBot, }; }, }; From c695123047b7d8d8b3930cbd31a2131552457553 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 09:26:03 +0100 Subject: [PATCH 09/20] [REF] awesome_clicker: refactor clicker state to a class model --- .../src/clicker_action/clicker_action.xml | 8 ++-- .../static/src/clicker_menu/clicker_menu.xml | 2 +- awesome_clicker/static/src/clicker_model.js | 31 +++++++++++++++ awesome_clicker/static/src/clicker_service.js | 39 +++---------------- awesome_clicker/static/src/utils.js | 6 +-- 5 files changed, 42 insertions(+), 44 deletions(-) create mode 100644 awesome_clicker/static/src/clicker_model.js diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index a3bf412f625..d79f77e7e5c 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -3,17 +3,17 @@
- +
-
+
Bots
- 1x ClickBots (10 clicks/10seconds) + x ClickBots (10 clicks/10seconds)
- +
diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index 990e78bc6e1..305e95e3e58 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -3,7 +3,7 @@
- +
diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js new file mode 100644 index 00000000000..79934714503 --- /dev/null +++ b/awesome_clicker/static/src/clicker_model.js @@ -0,0 +1,31 @@ +import { Reactive } from "@web/core/utils/reactive"; + +export class ClickerModel extends Reactive { + constructor() { + super(...arguments); + this.clicks = 0; + this.level = 0; + this.clickBots = 0; + } + + increment(val) { + this.clicks += val; + if (this.level < 1 && this.clicks >= 1000) { + this.level++; + } + } + + buyClickBot() { + const botPrice = 1000; + if (this.clicks < botPrice) { + return; + } + + this.clicks -= botPrice; + this.clickBots++; + } + + botsDoClicks(nbClicks) { + this.clicks += this.clickBots * nbClicks; + } +} diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index fc9553b7157..cc99384a99e 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -1,42 +1,13 @@ -import { reactive } from "@odoo/owl"; import { registry } from "@web/core/registry"; +import { ClickerModel } from "./clicker_model"; export const clickerService = { start() { - const state = reactive({ - clicks: 0, - level: 0, - clickBots: 0, - }); + const model = new ClickerModel(); - function increment(val) { - state.clicks += val; - if (state.level < 1 && state.clicks >= 1000) { - state.level++; - } - } - - function buyClickBot() { - const botPrice = 1000; - if (state.clicks < botPrice) { - return; - } - - state.clicks -= botPrice; - state.clickBots++; - } - - function botsDoClicks() { - state.clicks += state.clickBots * 10; - } - - document.addEventListener("click", () => increment(1), true); - setInterval(botsDoClicks, 10000); - return { - state, - increment, - buyClickBot, - }; + document.addEventListener("click", () => model.increment(1), true); + setInterval(() => model.botsDoClicks(10), 10000); + return model; }, }; diff --git a/awesome_clicker/static/src/utils.js b/awesome_clicker/static/src/utils.js index ef14a9dc765..d0343d98347 100644 --- a/awesome_clicker/static/src/utils.js +++ b/awesome_clicker/static/src/utils.js @@ -3,10 +3,6 @@ import { useService } from "@web/core/utils/hooks"; export function useClicker() { const service = useService("awesome_clicker.game_service"); - const state = useState(service.state); - return { - ...service, - state, - }; + return useState(service); } From 2b99d9e1c8f60561373b92454ab740ccaa0a63da Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 09:52:35 +0100 Subject: [PATCH 10/20] [IMP] awesome_clicker: add milestone effect --- awesome_clicker/static/src/clicker_model.js | 3 +++ awesome_clicker/static/src/clicker_service.js | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index 79934714503..1f67ea8eda8 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -1,3 +1,4 @@ +import { EventBus } from "@odoo/owl"; import { Reactive } from "@web/core/utils/reactive"; export class ClickerModel extends Reactive { @@ -6,11 +7,13 @@ export class ClickerModel extends Reactive { this.clicks = 0; this.level = 0; this.clickBots = 0; + this.bus = new EventBus(); } increment(val) { this.clicks += val; if (this.level < 1 && this.clicks >= 1000) { + this.bus.trigger("MILESTONE_1k"); this.level++; } } diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index cc99384a99e..1d41972f1e0 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -2,9 +2,13 @@ import { registry } from "@web/core/registry"; import { ClickerModel } from "./clicker_model"; export const clickerService = { - start() { + dependencies: ["effect"], + start(env, deps) { const model = new ClickerModel(); + model.bus.addEventListener("MILESTONE_1k", () => + deps.effect.add({ message: "Milestone reached! You can now buy clickbots." }) + ); document.addEventListener("click", () => model.increment(1), true); setInterval(() => model.botsDoClicks(10), 10000); return model; From 46353e936c4b784b67e45c01587dba8616a88f52 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 10:20:45 +0100 Subject: [PATCH 11/20] [IMP] awesome_clicker: add bigbots --- .../src/clicker_action/clicker_action.xml | 9 +++++++++ awesome_clicker/static/src/clicker_model.js | 18 ++++++++++++++++-- awesome_clicker/static/src/clicker_service.js | 13 +++++++++---- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index d79f77e7e5c..77c2f74b46b 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -16,6 +16,15 @@
+ +
+
+ x Bigbots (100 clicks/10seconds) +
+
+ +
+
diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index 1f67ea8eda8..86f12c2b944 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -7,6 +7,7 @@ export class ClickerModel extends Reactive { this.clicks = 0; this.level = 0; this.clickBots = 0; + this.bigBots = 0; this.bus = new EventBus(); } @@ -15,6 +16,9 @@ export class ClickerModel extends Reactive { if (this.level < 1 && this.clicks >= 1000) { this.bus.trigger("MILESTONE_1k"); this.level++; + } else if (this.level < 2 && this.clicks >= 5000) { + this.bus.trigger("MILESTONE_5k"); + this.level++; } } @@ -28,7 +32,17 @@ export class ClickerModel extends Reactive { this.clickBots++; } - botsDoClicks(nbClicks) { - this.clicks += this.clickBots * nbClicks; + buyBigBot() { + const botPrice = 5000; + if (this.clicks < botPrice) { + return; + } + + this.clicks -= botPrice; + this.bigBots++; + } + + botsDoClicks() { + this.clicks += this.clickBots * 10 + this.bigBots * 100; } } diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index 1d41972f1e0..127266b633e 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -4,13 +4,18 @@ import { ClickerModel } from "./clicker_model"; export const clickerService = { dependencies: ["effect"], start(env, deps) { + const MILESTONE_MESSAGES = { + MILESTONE_1k: "Milestone reached! You can now buy clickbots.", + MILESTONE_5k: "Milestone reached! You can now buy bigbots.", + }; + const model = new ClickerModel(); - model.bus.addEventListener("MILESTONE_1k", () => - deps.effect.add({ message: "Milestone reached! You can now buy clickbots." }) - ); + for (const [key, value] of Object.entries(MILESTONE_MESSAGES)) { + model.bus.addEventListener(key, () => deps.effect.add({ message: value })); + } document.addEventListener("click", () => model.increment(1), true); - setInterval(() => model.botsDoClicks(10), 10000); + setInterval(() => model.botsDoClicks(), 10000); return model; }, }; From 031b8d75890502558619506e2312180d2999a98e Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 10:50:43 +0100 Subject: [PATCH 12/20] [IMP] awesome_clicker: add power level increases --- .../static/src/clicker_action/clicker_action.xml | 12 +++++++++++- awesome_clicker/static/src/clicker_model.js | 16 +++++++++++++++- awesome_clicker/static/src/clicker_service.js | 1 + 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 77c2f74b46b..de1379ea2ea 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -4,7 +4,8 @@
- + +
Bots
@@ -25,6 +26,15 @@
+ +
+
+ x Power Level +
+
+ +
+
diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index 86f12c2b944..0ae9e946283 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -8,6 +8,7 @@ export class ClickerModel extends Reactive { this.level = 0; this.clickBots = 0; this.bigBots = 0; + this.power = 1; this.bus = new EventBus(); } @@ -19,6 +20,9 @@ export class ClickerModel extends Reactive { } else if (this.level < 2 && this.clicks >= 5000) { this.bus.trigger("MILESTONE_5k"); this.level++; + } else if (this.level < 3 && this.clicks >= 100000) { + this.bus.trigger("MILESTONE_100k"); + this.level++; } } @@ -42,7 +46,17 @@ export class ClickerModel extends Reactive { this.bigBots++; } + buyPower() { + const price = 50000; + if (this.clicks < price) { + return; + } + + this.clicks -= price; + this.power++; + } + botsDoClicks() { - this.clicks += this.clickBots * 10 + this.bigBots * 100; + this.clicks += (this.clickBots * 10 + this.bigBots * 100) * this.power; } } diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index 127266b633e..e04a25a429f 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -7,6 +7,7 @@ export const clickerService = { const MILESTONE_MESSAGES = { MILESTONE_1k: "Milestone reached! You can now buy clickbots.", MILESTONE_5k: "Milestone reached! You can now buy bigbots.", + MILESTONE_100k: "Milestone reached! You can now increase your power level.", }; const model = new ClickerModel(); From e978e4163727b0d9bc24bcdd72aabbc17d3cf11e Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 12:34:17 +0100 Subject: [PATCH 13/20] [REF] awesome_clicker: big refactoring - Extract game data such as shop items and milestones to a separate file for game data - Make the shop/action template more generic --- .../src/clicker_action/clicker_action.xml | 40 ++++------- awesome_clicker/static/src/clicker_data.js | 66 +++++++++++++++++++ awesome_clicker/static/src/clicker_model.js | 57 +++++++--------- awesome_clicker/static/src/clicker_service.js | 15 ++--- 4 files changed, 108 insertions(+), 70 deletions(-) create mode 100644 awesome_clicker/static/src/clicker_data.js diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index de1379ea2ea..10fde0b4b41 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -8,33 +8,21 @@
-
Bots
-
-
- x ClickBots (10 clicks/10seconds) +
Shop
+ +
+
+ + + +
+
+ +
-
- -
-
- -
-
- x Bigbots (100 clicks/10seconds) -
-
- -
-
- -
-
- x Power Level -
-
- -
-
+
diff --git a/awesome_clicker/static/src/clicker_data.js b/awesome_clicker/static/src/clicker_data.js new file mode 100644 index 00000000000..3b3e364d4d4 --- /dev/null +++ b/awesome_clicker/static/src/clicker_data.js @@ -0,0 +1,66 @@ +export const CLICKBOT_CLICKS = 10; +export const BIGBOT_CLICKS = 100; +export const BOT_FREQUENCY = 10000; // In milliseconds + +export const PURCHASABLE_REWARDS = [ + { + name: "clickbot", + icon: "fa-android", + clicks: CLICKBOT_CLICKS, + price: 1000, + minLevel: 1, + currentNumber: (clickerModel) => clickerModel.clickBots, + buy(clickerModel) { + if (clickerModel.verifyPurchase(1, 1000)) { + clickerModel.clickBots++; + } + }, + }, + { + name: "bigbot", + icon: "fa-android", + clicks: BIGBOT_CLICKS, + price: 5000, + minLevel: 2, + currentNumber: (clickerModel) => clickerModel.bigBots, + buy(clickerModel) { + if (clickerModel.verifyPurchase(2, 5000)) { + clickerModel.bigBots++; + } + }, + }, + { + name: "bot power", + icon: "fa-bolt", + price: 50000, + minLevel: 3, + currentNumber: (clickerModel) => clickerModel.multiplier, + buy(clickerModel) { + if (clickerModel.verifyPurchase(3, 50000)) { + clickerModel.multiplier++; + } + }, + }, +]; + +// Ordered milestones +export const MILESTONES = [ + { + level: 1, + clicks: 1000, + event: "MILESTONE_1k", + description: "You can now buy clickbots.", + }, + { + level: 2, + clicks: 5000, + event: "MILESTONE_5k", + description: "You can now buy bigbots.", + }, + { + level: 3, + clicks: 100000, + event: "MILESTONE_100k", + description: "You can now increase your bots' power.", + }, +]; diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index 0ae9e946283..f4c058c4f5b 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -1,5 +1,12 @@ import { EventBus } from "@odoo/owl"; import { Reactive } from "@web/core/utils/reactive"; +import { + BIGBOT_CLICKS, + BOT_FREQUENCY, + CLICKBOT_CLICKS, + MILESTONES, + PURCHASABLE_REWARDS, +} from "./clicker_data"; export class ClickerModel extends Reactive { constructor() { @@ -8,55 +15,35 @@ export class ClickerModel extends Reactive { this.level = 0; this.clickBots = 0; this.bigBots = 0; - this.power = 1; + this.multiplier = 1; + this.shopItems = PURCHASABLE_REWARDS; + this.botFrequency = BOT_FREQUENCY; this.bus = new EventBus(); } increment(val) { this.clicks += val; - if (this.level < 1 && this.clicks >= 1000) { - this.bus.trigger("MILESTONE_1k"); - this.level++; - } else if (this.level < 2 && this.clicks >= 5000) { - this.bus.trigger("MILESTONE_5k"); - this.level++; - } else if (this.level < 3 && this.clicks >= 100000) { - this.bus.trigger("MILESTONE_100k"); - this.level++; - } - } - buyClickBot() { - const botPrice = 1000; - if (this.clicks < botPrice) { - return; + for (const milestone of MILESTONES) { + if (this.level < milestone.level && this.clicks >= milestone.clicks) { + this.bus.trigger(milestone.event); + this.level = milestone.level; + break; + } } - - this.clicks -= botPrice; - this.clickBots++; - } - - buyBigBot() { - const botPrice = 5000; - if (this.clicks < botPrice) { - return; - } - - this.clicks -= botPrice; - this.bigBots++; } - buyPower() { - const price = 50000; - if (this.clicks < price) { - return; + verifyPurchase(minLevel, price) { + if (this.level < minLevel || this.clicks < price) { + return false; } this.clicks -= price; - this.power++; + return true; } botsDoClicks() { - this.clicks += (this.clickBots * 10 + this.bigBots * 100) * this.power; + this.clicks += + (this.clickBots * CLICKBOT_CLICKS + this.bigBots * BIGBOT_CLICKS) * this.multiplier; } } diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index e04a25a429f..72223ecfcf9 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -1,22 +1,19 @@ import { registry } from "@web/core/registry"; import { ClickerModel } from "./clicker_model"; +import { BOT_FREQUENCY, MILESTONES } from "./clicker_data"; export const clickerService = { dependencies: ["effect"], start(env, deps) { - const MILESTONE_MESSAGES = { - MILESTONE_1k: "Milestone reached! You can now buy clickbots.", - MILESTONE_5k: "Milestone reached! You can now buy bigbots.", - MILESTONE_100k: "Milestone reached! You can now increase your power level.", - }; - const model = new ClickerModel(); - for (const [key, value] of Object.entries(MILESTONE_MESSAGES)) { - model.bus.addEventListener(key, () => deps.effect.add({ message: value })); + for (const milestone of MILESTONES) { + model.bus.addEventListener(milestone.event, () => + deps.effect.add({ message: `Milestone reached! ${milestone.description}` }) + ); } document.addEventListener("click", () => model.increment(1), true); - setInterval(() => model.botsDoClicks(), 10000); + setInterval(() => model.botsDoClicks(), BOT_FREQUENCY); return model; }, }; From 1acb47a1a9d7c2ba2fc0709370102fcc7f7e8af8 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 12:48:50 +0100 Subject: [PATCH 14/20] [IMP] awesome_clicker: add random rewards --- .../src/clicker_action/clicker_action.xml | 3 +- awesome_clicker/static/src/clicker_data.js | 56 +++++++++++++++---- awesome_clicker/static/src/clicker_model.js | 7 +++ awesome_clicker/static/src/utils.js | 4 ++ 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 10fde0b4b41..4738f3fbedc 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -4,8 +4,7 @@
- - +
Shop
diff --git a/awesome_clicker/static/src/clicker_data.js b/awesome_clicker/static/src/clicker_data.js index 3b3e364d4d4..d2806988f39 100644 --- a/awesome_clicker/static/src/clicker_data.js +++ b/awesome_clicker/static/src/clicker_data.js @@ -9,10 +9,10 @@ export const PURCHASABLE_REWARDS = [ clicks: CLICKBOT_CLICKS, price: 1000, minLevel: 1, - currentNumber: (clickerModel) => clickerModel.clickBots, - buy(clickerModel) { - if (clickerModel.verifyPurchase(1, 1000)) { - clickerModel.clickBots++; + currentNumber: (clicker) => clicker.clickBots, + buy(clicker) { + if (clicker.verifyPurchase(1, 1000)) { + clicker.clickBots++; } }, }, @@ -22,10 +22,10 @@ export const PURCHASABLE_REWARDS = [ clicks: BIGBOT_CLICKS, price: 5000, minLevel: 2, - currentNumber: (clickerModel) => clickerModel.bigBots, - buy(clickerModel) { - if (clickerModel.verifyPurchase(2, 5000)) { - clickerModel.bigBots++; + currentNumber: (clicker) => clicker.bigBots, + buy(clicker) { + if (clicker.verifyPurchase(2, 5000)) { + clicker.bigBots++; } }, }, @@ -34,10 +34,10 @@ export const PURCHASABLE_REWARDS = [ icon: "fa-bolt", price: 50000, minLevel: 3, - currentNumber: (clickerModel) => clickerModel.multiplier, - buy(clickerModel) { - if (clickerModel.verifyPurchase(3, 50000)) { - clickerModel.multiplier++; + currentNumber: (clicker) => clicker.multiplier, + buy(clicker) { + if (clicker.verifyPurchase(3, 50000)) { + clicker.multiplier++; } }, }, @@ -64,3 +64,35 @@ export const MILESTONES = [ description: "You can now increase your bots' power.", }, ]; + +export const RANDOM_REWARDS = [ + { + description: "Get 3 click bots", + apply(clicker) { + clicker.clickBots += 3; + }, + minLevel: 1, + maxLevel: 2, + }, + { + description: "Get 3 big bots", + apply(clicker) { + clicker.bigBots += 3; + }, + minLevel: 2, + }, + { + description: "Get 10 big bots", + apply(clicker) { + clicker.bigBots += 10; + }, + minLevel: 3, + }, + { + description: "Increase bot power!", + apply(clicker) { + clicker.multipler += 1; + }, + minLevel: 3, + }, +]; diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index f4c058c4f5b..b643ac4f78d 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -6,7 +6,9 @@ import { CLICKBOT_CLICKS, MILESTONES, PURCHASABLE_REWARDS, + RANDOM_REWARDS, } from "./clicker_data"; +import { choose } from "./utils"; export class ClickerModel extends Reactive { constructor() { @@ -18,6 +20,7 @@ export class ClickerModel extends Reactive { this.multiplier = 1; this.shopItems = PURCHASABLE_REWARDS; this.botFrequency = BOT_FREQUENCY; + this.randomRewards = RANDOM_REWARDS; this.bus = new EventBus(); } @@ -42,6 +45,10 @@ export class ClickerModel extends Reactive { return true; } + getRandomReward() { + return choose(this.randomRewards); + } + botsDoClicks() { this.clicks += (this.clickBots * CLICKBOT_CLICKS + this.bigBots * BIGBOT_CLICKS) * this.multiplier; diff --git a/awesome_clicker/static/src/utils.js b/awesome_clicker/static/src/utils.js index d0343d98347..a0607487fe8 100644 --- a/awesome_clicker/static/src/utils.js +++ b/awesome_clicker/static/src/utils.js @@ -6,3 +6,7 @@ export function useClicker() { return useState(service); } + +export function choose(choices) { + return choices[Math.floor(Math.random() * choices.length)]; +} From 950bac5bc179c9e88c4d2f54f96ee054cef2ae7f Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 14:05:03 +0100 Subject: [PATCH 15/20] [IMP] awesome_clicker: add random reward on patched form controller --- awesome_clicker/static/src/clicker_data.js | 7 ++++ .../static/src/clicker_menu/clicker_menu.js | 8 +--- awesome_clicker/static/src/clicker_model.js | 23 ++++++++++-- awesome_clicker/static/src/clicker_service.js | 37 +++++++++++++++++-- .../src/patches/form_controller_patch.js | 12 ++++++ awesome_clicker/static/src/utils.js | 4 ++ 6 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 awesome_clicker/static/src/patches/form_controller_patch.js diff --git a/awesome_clicker/static/src/clicker_data.js b/awesome_clicker/static/src/clicker_data.js index d2806988f39..63fc1188e12 100644 --- a/awesome_clicker/static/src/clicker_data.js +++ b/awesome_clicker/static/src/clicker_data.js @@ -66,6 +66,13 @@ export const MILESTONES = [ ]; export const RANDOM_REWARDS = [ + { + description: "Get 1 click bot", + apply(clicker) { + clicker.clickBots += 1; + }, + maxLevel: 1, + }, { description: "Get 3 click bots", apply(clicker) { diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index 101f38907e8..503b6834d53 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -3,6 +3,7 @@ import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; import { useClicker } from "../utils"; import { ClickValue } from "../click_value"; +import { doClickerAction } from "../clicker_service"; export class ClickerMenu extends Component { static template = "awesome_clicker.clicker_menu"; @@ -14,12 +15,7 @@ export class ClickerMenu extends Component { } doAction() { - this.action.doAction({ - type: "ir.actions.client", - tag: "awesome_clicker.clicker_action", - target: "new", - name: "Clicker Game", - }); + doClickerAction(this.action); } } diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index b643ac4f78d..ffecb6da766 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -8,7 +8,7 @@ import { PURCHASABLE_REWARDS, RANDOM_REWARDS, } from "./clicker_data"; -import { choose } from "./utils"; +import { choose, randomBoolean } from "./utils"; export class ClickerModel extends Reactive { constructor() { @@ -20,10 +20,17 @@ export class ClickerModel extends Reactive { this.multiplier = 1; this.shopItems = PURCHASABLE_REWARDS; this.botFrequency = BOT_FREQUENCY; - this.randomRewards = RANDOM_REWARDS; this.bus = new EventBus(); } + _getApplicableRewards() { + return RANDOM_REWARDS.filter( + (r) => + (r.minLevel == null || this.level >= r.minLevel) && + (r.maxLevel == null || this.level <= r.maxLevel) + ); + } + increment(val) { this.clicks += val; @@ -45,8 +52,16 @@ export class ClickerModel extends Reactive { return true; } - getRandomReward() { - return choose(this.randomRewards); + giveRandomReward(chance = 1) { + if (!randomBoolean(chance)) { + return; + } + + const reward = choose(this._getApplicableRewards()); + // reward can be undefined if no applicable reward + if (reward) { + this.bus.trigger("REWARD", reward); + } } botsDoClicks() { diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index 72223ecfcf9..759a36a06c7 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -2,16 +2,47 @@ import { registry } from "@web/core/registry"; import { ClickerModel } from "./clicker_model"; import { BOT_FREQUENCY, MILESTONES } from "./clicker_data"; +export function doClickerAction(actionService) { + actionService.doAction({ + type: "ir.actions.client", + tag: "awesome_clicker.clicker_action", + target: "new", + name: "Clicker Game", + }); +} + export const clickerService = { - dependencies: ["effect"], - start(env, deps) { + dependencies: ["effect", "action", "notification"], + start(env, { effect, action, notification }) { const model = new ClickerModel(); for (const milestone of MILESTONES) { model.bus.addEventListener(milestone.event, () => - deps.effect.add({ message: `Milestone reached! ${milestone.description}` }) + effect.add({ message: `Milestone reached! ${milestone.description}` }) ); } + + model.bus.addEventListener("REWARD", (ev) => { + const closeNotif = notification.add( + `You've earned a reward: ${ev.detail.description}`, + { + title: "Clicker Reward", + type: "success", + sticky: true, + buttons: [ + { + name: "Collect", + onClick: () => { + closeNotif(); + ev.detail.apply(model); + doClickerAction(action); + }, + }, + ], + } + ); + }); + document.addEventListener("click", () => model.increment(1), true); setInterval(() => model.botsDoClicks(), BOT_FREQUENCY); return model; diff --git a/awesome_clicker/static/src/patches/form_controller_patch.js b/awesome_clicker/static/src/patches/form_controller_patch.js new file mode 100644 index 00000000000..3474c8aa3bb --- /dev/null +++ b/awesome_clicker/static/src/patches/form_controller_patch.js @@ -0,0 +1,12 @@ +import { patch } from "@web/core/utils/patch"; +import { FormController } from "@web/views/form/form_controller"; +import { useClicker } from "../utils"; + +const FormControllerPatch = { + setup() { + super.setup(...arguments); + useClicker().giveRandomReward(0.01); + }, +}; + +patch(FormController.prototype, FormControllerPatch); diff --git a/awesome_clicker/static/src/utils.js b/awesome_clicker/static/src/utils.js index a0607487fe8..4ce3667e2e8 100644 --- a/awesome_clicker/static/src/utils.js +++ b/awesome_clicker/static/src/utils.js @@ -10,3 +10,7 @@ export function useClicker() { export function choose(choices) { return choices[Math.floor(Math.random() * choices.length)]; } + +export function randomBoolean(trueChance) { + return Math.random() < trueChance; +} From 9c39ea775911a2adf43a41deb1ce55ab18e62501 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 14:31:34 +0100 Subject: [PATCH 16/20] [IMP] awesome_clicker: add commands --- .../static/src/clicker_commands.js | 19 +++++++++++++++++++ awesome_clicker/static/src/clicker_model.js | 7 +++++++ 2 files changed, 26 insertions(+) create mode 100644 awesome_clicker/static/src/clicker_commands.js diff --git a/awesome_clicker/static/src/clicker_commands.js b/awesome_clicker/static/src/clicker_commands.js new file mode 100644 index 00000000000..cc560c1ed85 --- /dev/null +++ b/awesome_clicker/static/src/clicker_commands.js @@ -0,0 +1,19 @@ +import { registry } from "@web/core/registry"; +import { doClickerAction } from "./clicker_service"; + +const commandRegistry = registry.category("command_provider"); + +commandRegistry.add("clicker", { + provide: (env, options) => [ + { + name: "Open Clicker Game", + action: () => doClickerAction(env.services.action), + }, + { + name: "Buy 1 click bot", + action: () => { + env.services["awesome_clicker.game_service"].purchase("clickbot"); + }, + }, + ], +}); diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index ffecb6da766..c19840f471c 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -52,6 +52,13 @@ export class ClickerModel extends Reactive { return true; } + purchase(name) { + const index = this.shopItems.findIndex((elem) => elem.name === name); + if (index >= 0) { + this.shopItems[index].buy(this); + } + } + giveRandomReward(chance = 1) { if (!randomBoolean(chance)) { return; From ee0cb49bab62fc894995bb7647413a86ba953996 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 15:21:24 +0100 Subject: [PATCH 17/20] [IMP] awesome_clicker: add fruits and trees --- .../src/clicker_action/clicker_action.xml | 15 ++++-- awesome_clicker/static/src/clicker_data.js | 47 +++++++++++++++++-- awesome_clicker/static/src/clicker_model.js | 21 ++++++++- awesome_clicker/static/src/clicker_service.js | 3 +- 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 4738f3fbedc..01c472daef9 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -4,15 +4,22 @@
- + + + +
+ +
+ +
Shop
- +
- - + +
diff --git a/awesome_clicker/static/src/clicker_data.js b/awesome_clicker/static/src/clicker_data.js index 63fc1188e12..7cc4a6d5da5 100644 --- a/awesome_clicker/static/src/clicker_data.js +++ b/awesome_clicker/static/src/clicker_data.js @@ -1,11 +1,14 @@ export const CLICKBOT_CLICKS = 10; export const BIGBOT_CLICKS = 100; export const BOT_FREQUENCY = 10000; // In milliseconds +export const TREE_FREQUENCY = 30000; // In milliseconds export const PURCHASABLE_REWARDS = [ { - name: "clickbot", + id: "clickbot", + name: "ClickBot", icon: "fa-android", + category: "Bots", clicks: CLICKBOT_CLICKS, price: 1000, minLevel: 1, @@ -17,8 +20,10 @@ export const PURCHASABLE_REWARDS = [ }, }, { - name: "bigbot", + id: "bigbot", + name: "BigBot", icon: "fa-android", + category: "Bots", clicks: BIGBOT_CLICKS, price: 5000, minLevel: 2, @@ -30,8 +35,10 @@ export const PURCHASABLE_REWARDS = [ }, }, { - name: "bot power", + id: "power-multiplier", + name: "Power Multiplier", icon: "fa-bolt", + category: "Bots", price: 50000, minLevel: 3, currentNumber: (clicker) => clicker.multiplier, @@ -41,6 +48,34 @@ export const PURCHASABLE_REWARDS = [ } }, }, + { + id: "pear-tree", + name: "Pear Tree", + icon: "fa-tree", + category: "Trees", + price: 1000000, + minLevel: 4, + currentNumber: (clicker) => clicker.pearTrees, + buy(clicker) { + if (clicker.verifyPurchase(4, 1000000)) { + clicker.pearTrees++; + } + }, + }, + { + id: "cherry-tree", + name: "Cherry Tree", + icon: "fa-tree", + category: "Trees", + price: 1000000, + minLevel: 4, + currentNumber: (clicker) => clicker.cherryTrees, + buy(clicker) { + if (clicker.verifyPurchase(4, 1000000)) { + clicker.cherryTrees++; + } + }, + }, ]; // Ordered milestones @@ -63,6 +98,12 @@ export const MILESTONES = [ event: "MILESTONE_100k", description: "You can now increase your bots' power.", }, + { + level: 4, + clicks: 1000000, + event: "MILESTONE_1m", + description: "You can now plant trees.", + }, ]; export const RANDOM_REWARDS = [ diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index c19840f471c..3f6487e883c 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -18,11 +18,23 @@ export class ClickerModel extends Reactive { this.clickBots = 0; this.bigBots = 0; this.multiplier = 1; + this.pearTrees = 0; + this.cherryTrees = 0; + this.pears = 0; + this.cherries = 0; this.shopItems = PURCHASABLE_REWARDS; this.botFrequency = BOT_FREQUENCY; this.bus = new EventBus(); } + get fruits() { + return this.pears + this.cherries; + } + + get trees() { + return this.pearTrees + this.cherryTrees; + } + _getApplicableRewards() { return RANDOM_REWARDS.filter( (r) => @@ -52,8 +64,8 @@ export class ClickerModel extends Reactive { return true; } - purchase(name) { - const index = this.shopItems.findIndex((elem) => elem.name === name); + purchase(id) { + const index = this.shopItems.findIndex((elem) => elem.id === id); if (index >= 0) { this.shopItems[index].buy(this); } @@ -75,4 +87,9 @@ export class ClickerModel extends Reactive { this.clicks += (this.clickBots * CLICKBOT_CLICKS + this.bigBots * BIGBOT_CLICKS) * this.multiplier; } + + treesProduceFruit() { + this.cherries += this.cherryTrees; + this.pears += this.pearTrees; + } } diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index 759a36a06c7..79d4649f140 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -1,6 +1,6 @@ import { registry } from "@web/core/registry"; import { ClickerModel } from "./clicker_model"; -import { BOT_FREQUENCY, MILESTONES } from "./clicker_data"; +import { BOT_FREQUENCY, MILESTONES, TREE_FREQUENCY } from "./clicker_data"; export function doClickerAction(actionService) { actionService.doAction({ @@ -45,6 +45,7 @@ export const clickerService = { document.addEventListener("click", () => model.increment(1), true); setInterval(() => model.botsDoClicks(), BOT_FREQUENCY); + setInterval(() => model.treesProduceFruit(), TREE_FREQUENCY); return model; }, }; From a9c4fdf809ed5b324f773348c60051b2059b73c3 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 16:10:22 +0100 Subject: [PATCH 18/20] [IMP] awesome_clicker: convert systray to dropdown --- awesome_clicker/static/src/click_value.js | 2 ++ .../src/clicker_action/clicker_action.xml | 6 ++-- awesome_clicker/static/src/clicker_data.js | 14 ++++++--- .../static/src/clicker_menu/clicker_menu.js | 4 ++- .../static/src/clicker_menu/clicker_menu.xml | 31 ++++++++++++++++--- awesome_clicker/static/src/clicker_model.js | 22 +++++++------ 6 files changed, 56 insertions(+), 23 deletions(-) diff --git a/awesome_clicker/static/src/click_value.js b/awesome_clicker/static/src/click_value.js index 65b89be5cfb..32a4b101aa9 100644 --- a/awesome_clicker/static/src/click_value.js +++ b/awesome_clicker/static/src/click_value.js @@ -4,12 +4,14 @@ import { humanNumber } from "@web/core/utils/numbers"; export class ClickValue extends Component { static props = { label: { type: String, optional: true }, + icon: { type: String, optional: true }, value: Number, }; static template = xml` + `; diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index 01c472daef9..e4fc68f3082 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -6,11 +6,9 @@ - +
- -
- +
diff --git a/awesome_clicker/static/src/clicker_data.js b/awesome_clicker/static/src/clicker_data.js index 7cc4a6d5da5..23053ad8537 100644 --- a/awesome_clicker/static/src/clicker_data.js +++ b/awesome_clicker/static/src/clicker_data.js @@ -55,10 +55,13 @@ export const PURCHASABLE_REWARDS = [ category: "Trees", price: 1000000, minLevel: 4, - currentNumber: (clicker) => clicker.pearTrees, + currentNumber: (clicker) => clicker.trees.pear ?? 0, buy(clicker) { if (clicker.verifyPurchase(4, 1000000)) { - clicker.pearTrees++; + if (!clicker.trees.pear) { + clicker.trees.pear = 0; + } + clicker.trees.pear++; } }, }, @@ -69,10 +72,13 @@ export const PURCHASABLE_REWARDS = [ category: "Trees", price: 1000000, minLevel: 4, - currentNumber: (clicker) => clicker.cherryTrees, + currentNumber: (clicker) => clicker.trees.cherry ?? 0, buy(clicker) { if (clicker.verifyPurchase(4, 1000000)) { - clicker.cherryTrees++; + if (!clicker.trees.cherry) { + clicker.trees.cherry = 0; + } + clicker.trees.cherry++; } }, }, diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.js b/awesome_clicker/static/src/clicker_menu/clicker_menu.js index 503b6834d53..5b43527af7f 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.js +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.js @@ -4,10 +4,12 @@ import { useService } from "@web/core/utils/hooks"; import { useClicker } from "../utils"; import { ClickValue } from "../click_value"; import { doClickerAction } from "../clicker_service"; +import { Dropdown } from "@web/core/dropdown/dropdown"; +import { DropdownItem } from "@web/core/dropdown/dropdown_item"; export class ClickerMenu extends Component { static template = "awesome_clicker.clicker_menu"; - static components = { ClickValue }; + static components = { ClickValue, Dropdown, DropdownItem }; setup() { this.clicker = useClicker(); diff --git a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml index 305e95e3e58..3897af4311d 100644 --- a/awesome_clicker/static/src/clicker_menu/clicker_menu.xml +++ b/awesome_clicker/static/src/clicker_menu/clicker_menu.xml @@ -2,10 +2,33 @@ -
- -
- + + + + + + + + + + + + + + + + +
diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index 3f6487e883c..c495f4e38d1 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -18,21 +18,19 @@ export class ClickerModel extends Reactive { this.clickBots = 0; this.bigBots = 0; this.multiplier = 1; - this.pearTrees = 0; - this.cherryTrees = 0; - this.pears = 0; - this.cherries = 0; + this.trees = {}; + this.fruits = {}; this.shopItems = PURCHASABLE_REWARDS; this.botFrequency = BOT_FREQUENCY; this.bus = new EventBus(); } - get fruits() { - return this.pears + this.cherries; + get totalFruits() { + return Object.values(this.fruits).reduce((a, b) => a + b, 0); } - get trees() { - return this.pearTrees + this.cherryTrees; + get totalTrees() { + return Object.values(this.trees).reduce((a, b) => a + b, 0); } _getApplicableRewards() { @@ -89,7 +87,11 @@ export class ClickerModel extends Reactive { } treesProduceFruit() { - this.cherries += this.cherryTrees; - this.pears += this.pearTrees; + for (const [type, nb] of Object.entries(this.trees)) { + if (!this.fruits[type]) { + this.fruits[type] = 0; + } + this.fruits[type] += nb; + } } } From f41c16e0ddf7460a5237d8af065a823c839b0fe1 Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 16:34:17 +0100 Subject: [PATCH 19/20] [IMP] awesome_clicker: display shop/action using notebook components --- .../src/clicker_action/clicker_action.js | 3 +- .../src/clicker_action/clicker_action.xml | 49 +++++++++++++------ awesome_clicker/static/src/clicker_model.js | 6 +++ 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.js b/awesome_clicker/static/src/clicker_action/clicker_action.js index 06167154e0e..4ec38f2d57c 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.js +++ b/awesome_clicker/static/src/clicker_action/clicker_action.js @@ -2,10 +2,11 @@ import { Component } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { useClicker } from "../utils"; import { ClickValue } from "../click_value"; +import { Notebook } from "@web/core/notebook/notebook"; export class ClickerAction extends Component { static template = "awesome_clicker.clicker_action"; - static components = { ClickValue }; + static components = { ClickValue, Notebook }; setup() { this.clicker = useClicker(); diff --git a/awesome_clicker/static/src/clicker_action/clicker_action.xml b/awesome_clicker/static/src/clicker_action/clicker_action.xml index e4fc68f3082..d27f30304d6 100644 --- a/awesome_clicker/static/src/clicker_action/clicker_action.xml +++ b/awesome_clicker/static/src/clicker_action/clicker_action.xml @@ -12,21 +12,40 @@
-
Shop
- -
-
- - - -
-
- -
-
-
+ + + +
+
+ + + +
+
+ +
+
+
+
+ + +
+
+ + + +
+
+ +
+
+
+
+
diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index c495f4e38d1..4fe5acd38e4 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -7,6 +7,7 @@ import { MILESTONES, PURCHASABLE_REWARDS, RANDOM_REWARDS, + TREE_FREQUENCY, } from "./clicker_data"; import { choose, randomBoolean } from "./utils"; @@ -22,6 +23,7 @@ export class ClickerModel extends Reactive { this.fruits = {}; this.shopItems = PURCHASABLE_REWARDS; this.botFrequency = BOT_FREQUENCY; + this.treeFrequency = TREE_FREQUENCY; this.bus = new EventBus(); } @@ -33,6 +35,10 @@ export class ClickerModel extends Reactive { return Object.values(this.trees).reduce((a, b) => a + b, 0); } + getItemsByCategory(category) { + return this.shopItems.filter((item) => item.category === category); + } + _getApplicableRewards() { return RANDOM_REWARDS.filter( (r) => From b289cb73d878d210a53e55bc57d52b2e62903f8c Mon Sep 17 00:00:00 2001 From: "Robin (robal)" Date: Fri, 26 Dec 2025 16:59:11 +0100 Subject: [PATCH 20/20] [IMP] awesome_clicker: add state persistence --- awesome_clicker/static/src/clicker_model.js | 16 ++++++++++++++++ awesome_clicker/static/src/clicker_service.js | 13 +++++++++++++ 2 files changed, 29 insertions(+) diff --git a/awesome_clicker/static/src/clicker_model.js b/awesome_clicker/static/src/clicker_model.js index 4fe5acd38e4..45cf6348f6c 100644 --- a/awesome_clicker/static/src/clicker_model.js +++ b/awesome_clicker/static/src/clicker_model.js @@ -35,6 +35,22 @@ export class ClickerModel extends Reactive { return Object.values(this.trees).reduce((a, b) => a + b, 0); } + get persistentState() { + return { + clicks: this.clicks, + level: this.level, + clickBots: this.clickBots, + bigBots: this.bigBots, + multiplier: this.multiplier, + trees: this.trees, + fruits: this.fruits, + }; + } + + set persistentState(state) { + Object.assign(this, state); + } + getItemsByCategory(category) { return this.shopItems.filter((item) => item.category === category); } diff --git a/awesome_clicker/static/src/clicker_service.js b/awesome_clicker/static/src/clicker_service.js index 79d4649f140..a5bfa6ecbca 100644 --- a/awesome_clicker/static/src/clicker_service.js +++ b/awesome_clicker/static/src/clicker_service.js @@ -1,6 +1,7 @@ import { registry } from "@web/core/registry"; import { ClickerModel } from "./clicker_model"; import { BOT_FREQUENCY, MILESTONES, TREE_FREQUENCY } from "./clicker_data"; +import { browser } from "@web/core/browser/browser"; export function doClickerAction(actionService) { actionService.doAction({ @@ -15,6 +16,10 @@ export const clickerService = { dependencies: ["effect", "action", "notification"], start(env, { effect, action, notification }) { const model = new ClickerModel(); + const persistentState = browser.localStorage.getItem("clicker_state"); + if (persistentState) { + model.persistentState = JSON.parse(persistentState); + } for (const milestone of MILESTONES) { model.bus.addEventListener(milestone.event, () => @@ -46,6 +51,14 @@ export const clickerService = { document.addEventListener("click", () => model.increment(1), true); setInterval(() => model.botsDoClicks(), BOT_FREQUENCY); setInterval(() => model.treesProduceFruit(), TREE_FREQUENCY); + setInterval( + () => + browser.localStorage.setItem( + "clicker_state", + JSON.stringify(model.persistentState) + ), + 10000 + ); return model; }, };